13aab70afSSebastian Siewior /* 23aab70afSSebastian Siewior * (C) Copyright 2008 - 2009 33aab70afSSebastian Siewior * Windriver, <www.windriver.com> 43aab70afSSebastian Siewior * Tom Rix <Tom.Rix@windriver.com> 53aab70afSSebastian Siewior * 63aab70afSSebastian Siewior * Copyright 2011 Sebastian Andrzej Siewior <bigeasy@linutronix.de> 73aab70afSSebastian Siewior * 83aab70afSSebastian Siewior * Copyright 2014 Linaro, Ltd. 93aab70afSSebastian Siewior * Rob Herring <robh@kernel.org> 103aab70afSSebastian Siewior * 113aab70afSSebastian Siewior * SPDX-License-Identifier: GPL-2.0+ 123aab70afSSebastian Siewior */ 13593cbd93SSteve Rae #include <config.h> 143aab70afSSebastian Siewior #include <common.h> 152e40c2c1SJason Zhu #include <console.h> 163aab70afSSebastian Siewior #include <errno.h> 173c8f98f5SMaxime Ripard #include <fastboot.h> 183aab70afSSebastian Siewior #include <malloc.h> 193aab70afSSebastian Siewior #include <linux/usb/ch9.h> 203aab70afSSebastian Siewior #include <linux/usb/gadget.h> 213aab70afSSebastian Siewior #include <linux/usb/composite.h> 223aab70afSSebastian Siewior #include <linux/compiler.h> 232e40c2c1SJason Zhu #include <u-boot/sha256.h> 243aab70afSSebastian Siewior #include <version.h> 253aab70afSSebastian Siewior #include <g_dnl.h> 262e40c2c1SJason Zhu #include <fs.h> 27367cce4dSXu Hongfei #include <android_avb/avb_ops_user.h> 2837a7bc39SJason Zhu #include <android_avb/rk_avb_ops_user.h> 292e40c2c1SJason Zhu #include <dm/uclass.h> 302e40c2c1SJason Zhu #include <power/fuel_gauge.h> 31d1b5ed07SSteve Rae #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV 32d1b5ed07SSteve Rae #include <fb_mmc.h> 33d1b5ed07SSteve Rae #endif 34bf8940d3SMaxime Ripard #ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV 35bf8940d3SMaxime Ripard #include <fb_nand.h> 36bf8940d3SMaxime Ripard #endif 374d0fc665SAndy Ye #ifdef CONFIG_OPTEE_CLIENT 384d0fc665SAndy Ye #include <optee_include/OpteeClientInterface.h> 394d0fc665SAndy Ye #endif 40f61a997eSqiujian #include <boot_rkimg.h> 41f61a997eSqiujian #include <optee_include/tee_client_api.h> 42*c2ba77d9SJian Qiu #ifdef CONFIG_FASTBOOT_OEM_UNLOCK 43*c2ba77d9SJian Qiu #include <keymaster.h> 44*c2ba77d9SJian Qiu #endif 453aab70afSSebastian Siewior 463aab70afSSebastian Siewior #define FASTBOOT_VERSION "0.4" 473aab70afSSebastian Siewior 483aab70afSSebastian Siewior #define FASTBOOT_INTERFACE_CLASS 0xff 493aab70afSSebastian Siewior #define FASTBOOT_INTERFACE_SUB_CLASS 0x42 503aab70afSSebastian Siewior #define FASTBOOT_INTERFACE_PROTOCOL 0x03 513aab70afSSebastian Siewior 523aab70afSSebastian Siewior #define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0 (0x0200) 533aab70afSSebastian Siewior #define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1 (0x0040) 543aab70afSSebastian Siewior #define TX_ENDPOINT_MAXIMUM_PACKET_SIZE (0x0040) 553aab70afSSebastian Siewior 563aab70afSSebastian Siewior #define EP_BUFFER_SIZE 4096 57de78ceaeSJason Zhu #define SLEEP_COUNT 20000 5890d27e7fSJason Zhu #define MAX_PART_NUM_STR_SIZE 4 5990d27e7fSJason Zhu #define PARTITION_TYPE_STRINGS "partition-type" 6090d27e7fSJason Zhu 61ac484c5aSRoger Quadros /* 62ac484c5aSRoger Quadros * EP_BUFFER_SIZE must always be an integral multiple of maxpacket size 63ac484c5aSRoger Quadros * (64 or 512 or 1024), else we break on certain controllers like DWC3 64ac484c5aSRoger Quadros * that expect bulk OUT requests to be divisible by maxpacket size. 65ac484c5aSRoger Quadros */ 663aab70afSSebastian Siewior 673aab70afSSebastian Siewior struct f_fastboot { 683aab70afSSebastian Siewior struct usb_function usb_function; 693aab70afSSebastian Siewior 70593cbd93SSteve Rae /* IN/OUT EP's and corresponding requests */ 713aab70afSSebastian Siewior struct usb_ep *in_ep, *out_ep; 723aab70afSSebastian Siewior struct usb_request *in_req, *out_req; 733aab70afSSebastian Siewior }; 743aab70afSSebastian Siewior 753aab70afSSebastian Siewior static inline struct f_fastboot *func_to_fastboot(struct usb_function *f) 763aab70afSSebastian Siewior { 773aab70afSSebastian Siewior return container_of(f, struct f_fastboot, usb_function); 783aab70afSSebastian Siewior } 793aab70afSSebastian Siewior 803aab70afSSebastian Siewior static struct f_fastboot *fastboot_func; 813aab70afSSebastian Siewior static unsigned int download_size; 823aab70afSSebastian Siewior static unsigned int download_bytes; 83367cce4dSXu Hongfei static unsigned int upload_size; 84367cce4dSXu Hongfei static unsigned int upload_bytes; 85367cce4dSXu Hongfei static bool start_upload; 86de78ceaeSJason Zhu static unsigned intthread_wakeup_needed; 873aab70afSSebastian Siewior 883aab70afSSebastian Siewior static struct usb_endpoint_descriptor fs_ep_in = { 893aab70afSSebastian Siewior .bLength = USB_DT_ENDPOINT_SIZE, 903aab70afSSebastian Siewior .bDescriptorType = USB_DT_ENDPOINT, 913aab70afSSebastian Siewior .bEndpointAddress = USB_DIR_IN, 923aab70afSSebastian Siewior .bmAttributes = USB_ENDPOINT_XFER_BULK, 93718156adSRoger Quadros .wMaxPacketSize = cpu_to_le16(64), 943aab70afSSebastian Siewior }; 953aab70afSSebastian Siewior 963aab70afSSebastian Siewior static struct usb_endpoint_descriptor fs_ep_out = { 973aab70afSSebastian Siewior .bLength = USB_DT_ENDPOINT_SIZE, 983aab70afSSebastian Siewior .bDescriptorType = USB_DT_ENDPOINT, 993aab70afSSebastian Siewior .bEndpointAddress = USB_DIR_OUT, 1003aab70afSSebastian Siewior .bmAttributes = USB_ENDPOINT_XFER_BULK, 101718156adSRoger Quadros .wMaxPacketSize = cpu_to_le16(64), 102718156adSRoger Quadros }; 103718156adSRoger Quadros 104718156adSRoger Quadros static struct usb_endpoint_descriptor hs_ep_in = { 105718156adSRoger Quadros .bLength = USB_DT_ENDPOINT_SIZE, 106718156adSRoger Quadros .bDescriptorType = USB_DT_ENDPOINT, 107718156adSRoger Quadros .bEndpointAddress = USB_DIR_IN, 108718156adSRoger Quadros .bmAttributes = USB_ENDPOINT_XFER_BULK, 109718156adSRoger Quadros .wMaxPacketSize = cpu_to_le16(512), 1103aab70afSSebastian Siewior }; 1113aab70afSSebastian Siewior 1123aab70afSSebastian Siewior static struct usb_endpoint_descriptor hs_ep_out = { 1133aab70afSSebastian Siewior .bLength = USB_DT_ENDPOINT_SIZE, 1143aab70afSSebastian Siewior .bDescriptorType = USB_DT_ENDPOINT, 1153aab70afSSebastian Siewior .bEndpointAddress = USB_DIR_OUT, 1163aab70afSSebastian Siewior .bmAttributes = USB_ENDPOINT_XFER_BULK, 117718156adSRoger Quadros .wMaxPacketSize = cpu_to_le16(512), 1183aab70afSSebastian Siewior }; 1193aab70afSSebastian Siewior 12026dd3474SWilliam Wu static struct usb_endpoint_descriptor ss_ep_in = { 12126dd3474SWilliam Wu .bLength = USB_DT_ENDPOINT_SIZE, 12226dd3474SWilliam Wu .bDescriptorType = USB_DT_ENDPOINT, 12326dd3474SWilliam Wu .bEndpointAddress = USB_DIR_IN, 12426dd3474SWilliam Wu .bmAttributes = USB_ENDPOINT_XFER_BULK, 12526dd3474SWilliam Wu .wMaxPacketSize = cpu_to_le16(1024), 12626dd3474SWilliam Wu }; 12726dd3474SWilliam Wu 12826dd3474SWilliam Wu static struct usb_ss_ep_comp_descriptor ss_ep_in_comp_desc = { 12926dd3474SWilliam Wu .bLength = sizeof(ss_ep_in_comp_desc), 13026dd3474SWilliam Wu .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 13126dd3474SWilliam Wu /* .bMaxBurst = DYNAMIC, */ 13226dd3474SWilliam Wu }; 13326dd3474SWilliam Wu 13426dd3474SWilliam Wu static struct usb_endpoint_descriptor ss_ep_out = { 13526dd3474SWilliam Wu .bLength = USB_DT_ENDPOINT_SIZE, 13626dd3474SWilliam Wu .bDescriptorType = USB_DT_ENDPOINT, 13726dd3474SWilliam Wu .bEndpointAddress = USB_DIR_OUT, 13826dd3474SWilliam Wu .bmAttributes = USB_ENDPOINT_XFER_BULK, 13926dd3474SWilliam Wu .wMaxPacketSize = cpu_to_le16(1024), 14026dd3474SWilliam Wu }; 14126dd3474SWilliam Wu 14226dd3474SWilliam Wu static struct usb_ss_ep_comp_descriptor ss_ep_out_comp_desc = { 14326dd3474SWilliam Wu .bLength = sizeof(ss_ep_out_comp_desc), 14426dd3474SWilliam Wu .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 14526dd3474SWilliam Wu /* .bMaxBurst = DYNAMIC, */ 14626dd3474SWilliam Wu }; 14726dd3474SWilliam Wu 1483aab70afSSebastian Siewior static struct usb_interface_descriptor interface_desc = { 1493aab70afSSebastian Siewior .bLength = USB_DT_INTERFACE_SIZE, 1503aab70afSSebastian Siewior .bDescriptorType = USB_DT_INTERFACE, 1513aab70afSSebastian Siewior .bInterfaceNumber = 0x00, 1523aab70afSSebastian Siewior .bAlternateSetting = 0x00, 1533aab70afSSebastian Siewior .bNumEndpoints = 0x02, 1543aab70afSSebastian Siewior .bInterfaceClass = FASTBOOT_INTERFACE_CLASS, 1553aab70afSSebastian Siewior .bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS, 1563aab70afSSebastian Siewior .bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL, 1573aab70afSSebastian Siewior }; 1583aab70afSSebastian Siewior 159718156adSRoger Quadros static struct usb_descriptor_header *fb_fs_function[] = { 1603aab70afSSebastian Siewior (struct usb_descriptor_header *)&interface_desc, 1613aab70afSSebastian Siewior (struct usb_descriptor_header *)&fs_ep_in, 162718156adSRoger Quadros (struct usb_descriptor_header *)&fs_ep_out, 163718156adSRoger Quadros }; 164718156adSRoger Quadros 165718156adSRoger Quadros static struct usb_descriptor_header *fb_hs_function[] = { 166718156adSRoger Quadros (struct usb_descriptor_header *)&interface_desc, 167718156adSRoger Quadros (struct usb_descriptor_header *)&hs_ep_in, 1683aab70afSSebastian Siewior (struct usb_descriptor_header *)&hs_ep_out, 1693aab70afSSebastian Siewior NULL, 1703aab70afSSebastian Siewior }; 1713aab70afSSebastian Siewior 17226dd3474SWilliam Wu static struct usb_descriptor_header *fb_ss_function[] = { 17326dd3474SWilliam Wu (struct usb_descriptor_header *)&interface_desc, 17426dd3474SWilliam Wu (struct usb_descriptor_header *)&ss_ep_in, 17526dd3474SWilliam Wu (struct usb_descriptor_header *)&ss_ep_in_comp_desc, 17626dd3474SWilliam Wu (struct usb_descriptor_header *)&ss_ep_out, 17726dd3474SWilliam Wu (struct usb_descriptor_header *)&ss_ep_out_comp_desc, 17826dd3474SWilliam Wu NULL, 17926dd3474SWilliam Wu }; 18026dd3474SWilliam Wu 1818b704a0eSRoger Quadros static struct usb_endpoint_descriptor * 1828b704a0eSRoger Quadros fb_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, 18326dd3474SWilliam Wu struct usb_endpoint_descriptor *hs, 18426dd3474SWilliam Wu struct usb_endpoint_descriptor *ss, 18526dd3474SWilliam Wu struct usb_ss_ep_comp_descriptor *comp_desc, 18626dd3474SWilliam Wu struct usb_ep *ep) 1878b704a0eSRoger Quadros { 18826dd3474SWilliam Wu struct usb_endpoint_descriptor *speed_desc = NULL; 18926dd3474SWilliam Wu 19026dd3474SWilliam Wu /* select desired speed */ 19126dd3474SWilliam Wu switch (g->speed) { 19226dd3474SWilliam Wu case USB_SPEED_SUPER: 19326dd3474SWilliam Wu if (gadget_is_superspeed(g)) { 19426dd3474SWilliam Wu speed_desc = ss; 19526dd3474SWilliam Wu ep->comp_desc = comp_desc; 19626dd3474SWilliam Wu break; 19726dd3474SWilliam Wu } 19826dd3474SWilliam Wu /* else: Fall trough */ 19926dd3474SWilliam Wu case USB_SPEED_HIGH: 20026dd3474SWilliam Wu if (gadget_is_dualspeed(g)) { 20126dd3474SWilliam Wu speed_desc = hs; 20226dd3474SWilliam Wu break; 20326dd3474SWilliam Wu } 20426dd3474SWilliam Wu /* else: fall through */ 20526dd3474SWilliam Wu default: 20626dd3474SWilliam Wu speed_desc = fs; 20726dd3474SWilliam Wu } 20826dd3474SWilliam Wu 20926dd3474SWilliam Wu return speed_desc; 2108b704a0eSRoger Quadros } 2118b704a0eSRoger Quadros 2123aab70afSSebastian Siewior /* 2133aab70afSSebastian Siewior * static strings, in UTF-8 2143aab70afSSebastian Siewior */ 2153aab70afSSebastian Siewior static const char fastboot_name[] = "Android Fastboot"; 2163aab70afSSebastian Siewior 2173aab70afSSebastian Siewior static struct usb_string fastboot_string_defs[] = { 2183aab70afSSebastian Siewior [0].s = fastboot_name, 2193aab70afSSebastian Siewior { } /* end of list */ 2203aab70afSSebastian Siewior }; 2213aab70afSSebastian Siewior 2223aab70afSSebastian Siewior static struct usb_gadget_strings stringtab_fastboot = { 2233aab70afSSebastian Siewior .language = 0x0409, /* en-us */ 2243aab70afSSebastian Siewior .strings = fastboot_string_defs, 2253aab70afSSebastian Siewior }; 2263aab70afSSebastian Siewior 2273aab70afSSebastian Siewior static struct usb_gadget_strings *fastboot_strings[] = { 2283aab70afSSebastian Siewior &stringtab_fastboot, 2293aab70afSSebastian Siewior NULL, 2303aab70afSSebastian Siewior }; 2313aab70afSSebastian Siewior 2323aab70afSSebastian Siewior static void rx_handler_command(struct usb_ep *ep, struct usb_request *req); 233e2ec3e46SAlexey Firago static int strcmp_l1(const char *s1, const char *s2); 234de78ceaeSJason Zhu static void wakeup_thread(void) 235de78ceaeSJason Zhu { 236de78ceaeSJason Zhu intthread_wakeup_needed = false; 237de78ceaeSJason Zhu } 238de78ceaeSJason Zhu 239de78ceaeSJason Zhu static void busy_indicator(void) 240de78ceaeSJason Zhu { 241de78ceaeSJason Zhu static int state; 242de78ceaeSJason Zhu 243de78ceaeSJason Zhu switch (state) { 244de78ceaeSJason Zhu case 0: 245de78ceaeSJason Zhu puts("\r|"); break; 246de78ceaeSJason Zhu case 1: 247de78ceaeSJason Zhu puts("\r/"); break; 248de78ceaeSJason Zhu case 2: 249de78ceaeSJason Zhu puts("\r-"); break; 250de78ceaeSJason Zhu case 3: 251de78ceaeSJason Zhu puts("\r\\"); break; 252de78ceaeSJason Zhu case 4: 253de78ceaeSJason Zhu puts("\r|"); break; 254de78ceaeSJason Zhu case 5: 255de78ceaeSJason Zhu puts("\r/"); break; 256de78ceaeSJason Zhu case 6: 257de78ceaeSJason Zhu puts("\r-"); break; 258de78ceaeSJason Zhu case 7: 259de78ceaeSJason Zhu puts("\r\\"); break; 260de78ceaeSJason Zhu default: 261de78ceaeSJason Zhu state = 0; 262de78ceaeSJason Zhu } 263de78ceaeSJason Zhu if (state++ == 8) 264de78ceaeSJason Zhu state = 0; 265de78ceaeSJason Zhu } 266de78ceaeSJason Zhu 26790d27e7fSJason Zhu static int fb_get_fstype(const char *ifname, const int part_num, 26890d27e7fSJason Zhu const char **fs_type) 26990d27e7fSJason Zhu { 27090d27e7fSJason Zhu char part_num_str[MAX_PART_NUM_STR_SIZE] = {0}; 27190d27e7fSJason Zhu 27290d27e7fSJason Zhu snprintf(part_num_str, ARRAY_SIZE(part_num_str), ":%x", part_num); 27390d27e7fSJason Zhu 27490d27e7fSJason Zhu if (fs_set_blk_dev(ifname, part_num_str, FS_TYPE_ANY)) 27590d27e7fSJason Zhu return -1; 27690d27e7fSJason Zhu 27790d27e7fSJason Zhu if (fs_get_fstype(fs_type)) 27890d27e7fSJason Zhu return -1; 27990d27e7fSJason Zhu 28090d27e7fSJason Zhu return 0; 28190d27e7fSJason Zhu } 28290d27e7fSJason Zhu 283de78ceaeSJason Zhu static int sleep_thread(void) 284de78ceaeSJason Zhu { 285de78ceaeSJason Zhu int rc = 0; 286de78ceaeSJason Zhu int i = 0, k = 0; 287de78ceaeSJason Zhu 288de78ceaeSJason Zhu /* Wait until a signal arrives or we are woken up */ 289de78ceaeSJason Zhu for (;;) { 290de78ceaeSJason Zhu if (!intthread_wakeup_needed) 291de78ceaeSJason Zhu break; 292de78ceaeSJason Zhu 293de78ceaeSJason Zhu if (++i == SLEEP_COUNT) { 294de78ceaeSJason Zhu busy_indicator(); 295de78ceaeSJason Zhu i = 0; 296de78ceaeSJason Zhu k++; 297de78ceaeSJason Zhu } 298de78ceaeSJason Zhu 299de78ceaeSJason Zhu if (k == 10) { 300de78ceaeSJason Zhu /* Handle CTRL+C */ 301de78ceaeSJason Zhu if (ctrlc()) 302de78ceaeSJason Zhu return -EPIPE; 303de78ceaeSJason Zhu 304de78ceaeSJason Zhu /* Check cable connection */ 305de78ceaeSJason Zhu if (!g_dnl_board_usb_cable_connected()) 306de78ceaeSJason Zhu return -EIO; 307de78ceaeSJason Zhu 308de78ceaeSJason Zhu k = 0; 309de78ceaeSJason Zhu } 310de78ceaeSJason Zhu 311de78ceaeSJason Zhu usb_gadget_handle_interrupts(0); 312de78ceaeSJason Zhu } 313de78ceaeSJason Zhu intthread_wakeup_needed = true; 314de78ceaeSJason Zhu return rc; 315de78ceaeSJason Zhu } 3163aab70afSSebastian Siewior 3173aab70afSSebastian Siewior static void fastboot_complete(struct usb_ep *ep, struct usb_request *req) 3183aab70afSSebastian Siewior { 3193aab70afSSebastian Siewior int status = req->status; 320de78ceaeSJason Zhu 321de78ceaeSJason Zhu wakeup_thread(); 3223aab70afSSebastian Siewior if (!status) 3233aab70afSSebastian Siewior return; 3243aab70afSSebastian Siewior printf("status: %d ep '%s' trans: %d\n", status, ep->name, req->actual); 3253aab70afSSebastian Siewior } 3263aab70afSSebastian Siewior 3273aab70afSSebastian Siewior static int fastboot_bind(struct usb_configuration *c, struct usb_function *f) 3283aab70afSSebastian Siewior { 3293aab70afSSebastian Siewior int id; 3303aab70afSSebastian Siewior struct usb_gadget *gadget = c->cdev->gadget; 3313aab70afSSebastian Siewior struct f_fastboot *f_fb = func_to_fastboot(f); 332537cd072SDileep Katta const char *s; 3333aab70afSSebastian Siewior 3343aab70afSSebastian Siewior /* DYNAMIC interface numbers assignments */ 3353aab70afSSebastian Siewior id = usb_interface_id(c, f); 3363aab70afSSebastian Siewior if (id < 0) 3373aab70afSSebastian Siewior return id; 3383aab70afSSebastian Siewior interface_desc.bInterfaceNumber = id; 3393aab70afSSebastian Siewior 3403aab70afSSebastian Siewior id = usb_string_id(c->cdev); 3413aab70afSSebastian Siewior if (id < 0) 3423aab70afSSebastian Siewior return id; 3433aab70afSSebastian Siewior fastboot_string_defs[0].id = id; 3443aab70afSSebastian Siewior interface_desc.iInterface = id; 3453aab70afSSebastian Siewior 3463aab70afSSebastian Siewior f_fb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in); 3473aab70afSSebastian Siewior if (!f_fb->in_ep) 3483aab70afSSebastian Siewior return -ENODEV; 3493aab70afSSebastian Siewior f_fb->in_ep->driver_data = c->cdev; 3503aab70afSSebastian Siewior 3513aab70afSSebastian Siewior f_fb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out); 3523aab70afSSebastian Siewior if (!f_fb->out_ep) 3533aab70afSSebastian Siewior return -ENODEV; 3543aab70afSSebastian Siewior f_fb->out_ep->driver_data = c->cdev; 3553aab70afSSebastian Siewior 356718156adSRoger Quadros f->descriptors = fb_fs_function; 357718156adSRoger Quadros 358718156adSRoger Quadros if (gadget_is_dualspeed(gadget)) { 359718156adSRoger Quadros /* Assume endpoint addresses are the same for both speeds */ 360718156adSRoger Quadros hs_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress; 3613aab70afSSebastian Siewior hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; 362718156adSRoger Quadros /* copy HS descriptors */ 363718156adSRoger Quadros f->hs_descriptors = fb_hs_function; 364718156adSRoger Quadros } 3653aab70afSSebastian Siewior 36626dd3474SWilliam Wu if (gadget_is_superspeed(gadget)) { 36726dd3474SWilliam Wu /* Assume endpoint addresses are the same as full speed */ 36826dd3474SWilliam Wu ss_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress; 36926dd3474SWilliam Wu ss_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; 37026dd3474SWilliam Wu /* copy SS descriptors */ 37126dd3474SWilliam Wu f->ss_descriptors = fb_ss_function; 37226dd3474SWilliam Wu } 37326dd3474SWilliam Wu 37400caae6dSSimon Glass s = env_get("serial#"); 375537cd072SDileep Katta if (s) 376537cd072SDileep Katta g_dnl_set_serialnumber((char *)s); 377537cd072SDileep Katta 3783aab70afSSebastian Siewior return 0; 3793aab70afSSebastian Siewior } 3803aab70afSSebastian Siewior 3813aab70afSSebastian Siewior static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f) 3823aab70afSSebastian Siewior { 3833aab70afSSebastian Siewior memset(fastboot_func, 0, sizeof(*fastboot_func)); 3843aab70afSSebastian Siewior } 3853aab70afSSebastian Siewior 3863aab70afSSebastian Siewior static void fastboot_disable(struct usb_function *f) 3873aab70afSSebastian Siewior { 3883aab70afSSebastian Siewior struct f_fastboot *f_fb = func_to_fastboot(f); 3893aab70afSSebastian Siewior 3903aab70afSSebastian Siewior usb_ep_disable(f_fb->out_ep); 3913aab70afSSebastian Siewior usb_ep_disable(f_fb->in_ep); 3923aab70afSSebastian Siewior 3933aab70afSSebastian Siewior if (f_fb->out_req) { 3943aab70afSSebastian Siewior free(f_fb->out_req->buf); 3953aab70afSSebastian Siewior usb_ep_free_request(f_fb->out_ep, f_fb->out_req); 3963aab70afSSebastian Siewior f_fb->out_req = NULL; 3973aab70afSSebastian Siewior } 3983aab70afSSebastian Siewior if (f_fb->in_req) { 3993aab70afSSebastian Siewior free(f_fb->in_req->buf); 4003aab70afSSebastian Siewior usb_ep_free_request(f_fb->in_ep, f_fb->in_req); 4013aab70afSSebastian Siewior f_fb->in_req = NULL; 4023aab70afSSebastian Siewior } 4033aab70afSSebastian Siewior } 4043aab70afSSebastian Siewior 4053aab70afSSebastian Siewior static struct usb_request *fastboot_start_ep(struct usb_ep *ep) 4063aab70afSSebastian Siewior { 4073aab70afSSebastian Siewior struct usb_request *req; 4083aab70afSSebastian Siewior 4093aab70afSSebastian Siewior req = usb_ep_alloc_request(ep, 0); 4103aab70afSSebastian Siewior if (!req) 4113aab70afSSebastian Siewior return NULL; 4123aab70afSSebastian Siewior 4133aab70afSSebastian Siewior req->length = EP_BUFFER_SIZE; 4143aab70afSSebastian Siewior req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, EP_BUFFER_SIZE); 4153aab70afSSebastian Siewior if (!req->buf) { 4163aab70afSSebastian Siewior usb_ep_free_request(ep, req); 4173aab70afSSebastian Siewior return NULL; 4183aab70afSSebastian Siewior } 4193aab70afSSebastian Siewior 4203aab70afSSebastian Siewior memset(req->buf, 0, req->length); 4213aab70afSSebastian Siewior return req; 4223aab70afSSebastian Siewior } 4233aab70afSSebastian Siewior 4243aab70afSSebastian Siewior static int fastboot_set_alt(struct usb_function *f, 4253aab70afSSebastian Siewior unsigned interface, unsigned alt) 4263aab70afSSebastian Siewior { 4273aab70afSSebastian Siewior int ret; 4283aab70afSSebastian Siewior struct usb_composite_dev *cdev = f->config->cdev; 4293aab70afSSebastian Siewior struct usb_gadget *gadget = cdev->gadget; 4303aab70afSSebastian Siewior struct f_fastboot *f_fb = func_to_fastboot(f); 4318b704a0eSRoger Quadros const struct usb_endpoint_descriptor *d; 4323aab70afSSebastian Siewior 4333aab70afSSebastian Siewior debug("%s: func: %s intf: %d alt: %d\n", 4343aab70afSSebastian Siewior __func__, f->name, interface, alt); 4353aab70afSSebastian Siewior 43626dd3474SWilliam Wu d = fb_ep_desc(gadget, &fs_ep_out, &hs_ep_out, &ss_ep_out, 43726dd3474SWilliam Wu &ss_ep_out_comp_desc, f_fb->out_ep); 4388b704a0eSRoger Quadros ret = usb_ep_enable(f_fb->out_ep, d); 4393aab70afSSebastian Siewior if (ret) { 4403aab70afSSebastian Siewior puts("failed to enable out ep\n"); 4413aab70afSSebastian Siewior return ret; 4423aab70afSSebastian Siewior } 4433aab70afSSebastian Siewior 4443aab70afSSebastian Siewior f_fb->out_req = fastboot_start_ep(f_fb->out_ep); 4453aab70afSSebastian Siewior if (!f_fb->out_req) { 4463aab70afSSebastian Siewior puts("failed to alloc out req\n"); 4473aab70afSSebastian Siewior ret = -EINVAL; 4483aab70afSSebastian Siewior goto err; 4493aab70afSSebastian Siewior } 4503aab70afSSebastian Siewior f_fb->out_req->complete = rx_handler_command; 4513aab70afSSebastian Siewior 45226dd3474SWilliam Wu d = fb_ep_desc(gadget, &fs_ep_in, &hs_ep_in, &ss_ep_in, 45326dd3474SWilliam Wu &ss_ep_in_comp_desc, f_fb->in_ep); 4548b704a0eSRoger Quadros ret = usb_ep_enable(f_fb->in_ep, d); 4553aab70afSSebastian Siewior if (ret) { 4563aab70afSSebastian Siewior puts("failed to enable in ep\n"); 4573aab70afSSebastian Siewior goto err; 4583aab70afSSebastian Siewior } 4593aab70afSSebastian Siewior 4603aab70afSSebastian Siewior f_fb->in_req = fastboot_start_ep(f_fb->in_ep); 4613aab70afSSebastian Siewior if (!f_fb->in_req) { 4623aab70afSSebastian Siewior puts("failed alloc req in\n"); 4633aab70afSSebastian Siewior ret = -EINVAL; 4643aab70afSSebastian Siewior goto err; 4653aab70afSSebastian Siewior } 4663aab70afSSebastian Siewior f_fb->in_req->complete = fastboot_complete; 4673aab70afSSebastian Siewior 4683aab70afSSebastian Siewior ret = usb_ep_queue(f_fb->out_ep, f_fb->out_req, 0); 4693aab70afSSebastian Siewior if (ret) 4703aab70afSSebastian Siewior goto err; 4713aab70afSSebastian Siewior 4723aab70afSSebastian Siewior return 0; 4733aab70afSSebastian Siewior err: 4743aab70afSSebastian Siewior fastboot_disable(f); 4753aab70afSSebastian Siewior return ret; 4763aab70afSSebastian Siewior } 4773aab70afSSebastian Siewior 4783aab70afSSebastian Siewior static int fastboot_add(struct usb_configuration *c) 4793aab70afSSebastian Siewior { 4803aab70afSSebastian Siewior struct f_fastboot *f_fb = fastboot_func; 4813aab70afSSebastian Siewior int status; 4823aab70afSSebastian Siewior 4833aab70afSSebastian Siewior debug("%s: cdev: 0x%p\n", __func__, c->cdev); 4843aab70afSSebastian Siewior 4853aab70afSSebastian Siewior if (!f_fb) { 4863aab70afSSebastian Siewior f_fb = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_fb)); 4873aab70afSSebastian Siewior if (!f_fb) 4883aab70afSSebastian Siewior return -ENOMEM; 4893aab70afSSebastian Siewior 4903aab70afSSebastian Siewior fastboot_func = f_fb; 4913aab70afSSebastian Siewior memset(f_fb, 0, sizeof(*f_fb)); 4923aab70afSSebastian Siewior } 4933aab70afSSebastian Siewior 4943aab70afSSebastian Siewior f_fb->usb_function.name = "f_fastboot"; 4953aab70afSSebastian Siewior f_fb->usb_function.bind = fastboot_bind; 4963aab70afSSebastian Siewior f_fb->usb_function.unbind = fastboot_unbind; 4973aab70afSSebastian Siewior f_fb->usb_function.set_alt = fastboot_set_alt; 4983aab70afSSebastian Siewior f_fb->usb_function.disable = fastboot_disable; 4993aab70afSSebastian Siewior f_fb->usb_function.strings = fastboot_strings; 5003aab70afSSebastian Siewior 5013aab70afSSebastian Siewior status = usb_add_function(c, &f_fb->usb_function); 5023aab70afSSebastian Siewior if (status) { 5033aab70afSSebastian Siewior free(f_fb); 5043aab70afSSebastian Siewior fastboot_func = f_fb; 5053aab70afSSebastian Siewior } 5063aab70afSSebastian Siewior 5073aab70afSSebastian Siewior return status; 5083aab70afSSebastian Siewior } 5093aab70afSSebastian Siewior DECLARE_GADGET_BIND_CALLBACK(usb_dnl_fastboot, fastboot_add); 5103aab70afSSebastian Siewior 511593cbd93SSteve Rae static int fastboot_tx_write(const char *buffer, unsigned int buffer_size) 5123aab70afSSebastian Siewior { 5133aab70afSSebastian Siewior struct usb_request *in_req = fastboot_func->in_req; 5143aab70afSSebastian Siewior int ret; 5153aab70afSSebastian Siewior 5163aab70afSSebastian Siewior memcpy(in_req->buf, buffer, buffer_size); 5173aab70afSSebastian Siewior in_req->length = buffer_size; 518bc9071c9SPaul Kocialkowski 519bc9071c9SPaul Kocialkowski usb_ep_dequeue(fastboot_func->in_ep, in_req); 520bc9071c9SPaul Kocialkowski 5213aab70afSSebastian Siewior ret = usb_ep_queue(fastboot_func->in_ep, in_req, 0); 5223aab70afSSebastian Siewior if (ret) 5233aab70afSSebastian Siewior printf("Error %d on queue\n", ret); 5243aab70afSSebastian Siewior return 0; 5253aab70afSSebastian Siewior } 5263aab70afSSebastian Siewior 5273aab70afSSebastian Siewior static int fastboot_tx_write_str(const char *buffer) 5283aab70afSSebastian Siewior { 529de78ceaeSJason Zhu int ret; 530de78ceaeSJason Zhu 531de78ceaeSJason Zhu ret = sleep_thread(); 532de78ceaeSJason Zhu if (ret < 0) 533de78ceaeSJason Zhu printf("warning: 0x%x, usb transmission is abnormal!\n", ret); 534de78ceaeSJason Zhu 5353aab70afSSebastian Siewior return fastboot_tx_write(buffer, strlen(buffer)); 5363aab70afSSebastian Siewior } 5373aab70afSSebastian Siewior 5383aab70afSSebastian Siewior static void compl_do_reset(struct usb_ep *ep, struct usb_request *req) 5393aab70afSSebastian Siewior { 5403aab70afSSebastian Siewior do_reset(NULL, 0, 0, NULL); 5413aab70afSSebastian Siewior } 5423aab70afSSebastian Siewior 543e2ec3e46SAlexey Firago int __weak fb_set_reboot_flag(void) 544e2ec3e46SAlexey Firago { 545e2ec3e46SAlexey Firago return -ENOSYS; 546e2ec3e46SAlexey Firago } 547e2ec3e46SAlexey Firago 5483aab70afSSebastian Siewior static void cb_reboot(struct usb_ep *ep, struct usb_request *req) 5493aab70afSSebastian Siewior { 550e2ec3e46SAlexey Firago char *cmd = req->buf; 551e2ec3e46SAlexey Firago if (!strcmp_l1("reboot-bootloader", cmd)) { 552e2ec3e46SAlexey Firago if (fb_set_reboot_flag()) { 553e2ec3e46SAlexey Firago fastboot_tx_write_str("FAILCannot set reboot flag"); 554e2ec3e46SAlexey Firago return; 555e2ec3e46SAlexey Firago } 556e2ec3e46SAlexey Firago } 5573aab70afSSebastian Siewior fastboot_func->in_req->complete = compl_do_reset; 5583aab70afSSebastian Siewior fastboot_tx_write_str("OKAY"); 5593aab70afSSebastian Siewior } 5603aab70afSSebastian Siewior 5613aab70afSSebastian Siewior static int strcmp_l1(const char *s1, const char *s2) 5623aab70afSSebastian Siewior { 5633aab70afSSebastian Siewior if (!s1 || !s2) 5643aab70afSSebastian Siewior return -1; 5653aab70afSSebastian Siewior return strncmp(s1, s2, strlen(s1)); 5663aab70afSSebastian Siewior } 5673aab70afSSebastian Siewior 56890d27e7fSJason Zhu struct name_string { 56990d27e7fSJason Zhu const char *str; 57090d27e7fSJason Zhu int expects_args; 57190d27e7fSJason Zhu char delim; 57290d27e7fSJason Zhu }; 57390d27e7fSJason Zhu 57490d27e7fSJason Zhu #define NAME_NO_ARGS(s) {.str = s, .expects_args = 0} 57590d27e7fSJason Zhu #define NAME_ARGS(s, d) {.str = s, .expects_args = 1, .delim = d} 57690d27e7fSJason Zhu 57790d27e7fSJason Zhu static size_t name_check_match(const char *str, size_t len, 57890d27e7fSJason Zhu const struct name_string *name) 5793aab70afSSebastian Siewior { 58090d27e7fSJason Zhu size_t str_len = strlen(name->str); 5813aab70afSSebastian Siewior 58290d27e7fSJason Zhu /* If name len is greater than input, return 0. */ 58390d27e7fSJason Zhu if (str_len > len) 58490d27e7fSJason Zhu return 0; 58529425be4SJeroen Hofstee 58690d27e7fSJason Zhu /* If name str does not match input string, return 0. */ 58790d27e7fSJason Zhu if (memcmp(name->str, str, str_len)) 58890d27e7fSJason Zhu return 0; 58990d27e7fSJason Zhu 59090d27e7fSJason Zhu if (name->expects_args) { 59190d27e7fSJason Zhu /* string should have space for delim */ 59290d27e7fSJason Zhu if (len == str_len) 59390d27e7fSJason Zhu return 0; 59490d27e7fSJason Zhu 59590d27e7fSJason Zhu /* Check delim match */ 59690d27e7fSJason Zhu if (name->delim != str[str_len]) 59790d27e7fSJason Zhu return 0; 59890d27e7fSJason Zhu } else { 59990d27e7fSJason Zhu /* Name str len should match input len */ 60090d27e7fSJason Zhu if (str_len != len) 60190d27e7fSJason Zhu return 0; 6023aab70afSSebastian Siewior } 6033aab70afSSebastian Siewior 60490d27e7fSJason Zhu return str_len + name->expects_args; 60590d27e7fSJason Zhu } 6063aab70afSSebastian Siewior 60790d27e7fSJason Zhu static void fb_add_string(char *dst, size_t chars_left, 60890d27e7fSJason Zhu const char *str, const char *args) 60990d27e7fSJason Zhu { 61090d27e7fSJason Zhu if (!str) 61190d27e7fSJason Zhu return; 61290d27e7fSJason Zhu 61390d27e7fSJason Zhu int ret = snprintf(dst, chars_left, str, args); 61490d27e7fSJason Zhu 61590d27e7fSJason Zhu if (ret < 0) 61690d27e7fSJason Zhu pr_err("snprintf is error!"); 61790d27e7fSJason Zhu } 61890d27e7fSJason Zhu 61990d27e7fSJason Zhu static void fb_add_number(char *dst, size_t chars_left, 62090d27e7fSJason Zhu const char *format, size_t num) 62190d27e7fSJason Zhu { 62290d27e7fSJason Zhu if (!format) 62390d27e7fSJason Zhu return; 62490d27e7fSJason Zhu 62590d27e7fSJason Zhu int ret = snprintf(dst, chars_left, format, num); 62690d27e7fSJason Zhu 62790d27e7fSJason Zhu if (ret > chars_left) 62890d27e7fSJason Zhu pr_err("snprintf is error!"); 62990d27e7fSJason Zhu } 63090d27e7fSJason Zhu 63190d27e7fSJason Zhu static int fb_read_var(char *cmd, char *response, 63290d27e7fSJason Zhu fb_getvar_t var, size_t chars_left) 63390d27e7fSJason Zhu { 63490d27e7fSJason Zhu const char *s; 63590d27e7fSJason Zhu int ret = 0; 63690d27e7fSJason Zhu 63790d27e7fSJason Zhu switch (var) { 63890d27e7fSJason Zhu case FB_VERSION: 63990d27e7fSJason Zhu fb_add_string(response, chars_left, FASTBOOT_VERSION, NULL); 64090d27e7fSJason Zhu break; 64190d27e7fSJason Zhu case FB_BOOTLOADER_VERSION: 64290d27e7fSJason Zhu fb_add_string(response, chars_left, U_BOOT_VERSION, NULL); 64390d27e7fSJason Zhu break; 64490d27e7fSJason Zhu case FB_BASEBAND_VERSION: 64590d27e7fSJason Zhu fb_add_string(response, chars_left, "N/A", NULL); 64690d27e7fSJason Zhu break; 64790d27e7fSJason Zhu case FB_PRODUCT: 64890d27e7fSJason Zhu fb_add_string(response, chars_left, CONFIG_SYS_BOARD, NULL); 64990d27e7fSJason Zhu break; 65090d27e7fSJason Zhu case FB_SERIAL_NO: 65100caae6dSSimon Glass s = env_get("serial#"); 6523aab70afSSebastian Siewior if (s) 65390d27e7fSJason Zhu fb_add_string(response, chars_left, s, NULL); 6543aab70afSSebastian Siewior else 65590d27e7fSJason Zhu ret = -1; 65690d27e7fSJason Zhu break; 65790d27e7fSJason Zhu case FB_SECURE: 65890d27e7fSJason Zhu fb_add_string(response, chars_left, "yes", NULL); 65990d27e7fSJason Zhu break; 66090d27e7fSJason Zhu case FB_VARIANT: 66190d27e7fSJason Zhu fb_add_string(response, chars_left, "userdebug", NULL); 66290d27e7fSJason Zhu break; 66390d27e7fSJason Zhu case FB_DWNLD_SIZE: 66490d27e7fSJason Zhu fb_add_number(response, chars_left, "0x%08x", 66590d27e7fSJason Zhu CONFIG_FASTBOOT_BUF_SIZE); 66690d27e7fSJason Zhu break; 66790d27e7fSJason Zhu case FB_PART_SIZE: 66890d27e7fSJason Zhu case FB_PART_TYPE: { 66990d27e7fSJason Zhu char *part_name = cmd; 670367cce4dSXu Hongfei 67190d27e7fSJason Zhu cmd = strsep(&part_name, ":"); 67290d27e7fSJason Zhu if (!cmd || !part_name) { 67390d27e7fSJason Zhu fb_add_string(response, chars_left, 67490d27e7fSJason Zhu "argument Invalid!", NULL); 67590d27e7fSJason Zhu ret = -1; 67690d27e7fSJason Zhu break; 67790d27e7fSJason Zhu } 67890d27e7fSJason Zhu 67990d27e7fSJason Zhu #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV 68090d27e7fSJason Zhu disk_partition_t part_info; 68190d27e7fSJason Zhu struct blk_desc *dev_desc; 68290d27e7fSJason Zhu int part_num = -1; 68390d27e7fSJason Zhu const char *fs_type = NULL; 68490d27e7fSJason Zhu 68590d27e7fSJason Zhu #ifdef CONFIG_RKIMG_BOOTLOADER 68690d27e7fSJason Zhu dev_desc = rockchip_get_bootdev(); 68790d27e7fSJason Zhu #else 68890d27e7fSJason Zhu dev_desc = NULL; 68990d27e7fSJason Zhu #endif 69090d27e7fSJason Zhu if (!dev_desc) { 69190d27e7fSJason Zhu fb_add_string(response, chars_left, 69290d27e7fSJason Zhu "block device not found", NULL); 69390d27e7fSJason Zhu ret = -1; 69490d27e7fSJason Zhu break; 69590d27e7fSJason Zhu } 69690d27e7fSJason Zhu 69790d27e7fSJason Zhu part_num = part_get_info_by_name(dev_desc, part_name, 69890d27e7fSJason Zhu &part_info); 69990d27e7fSJason Zhu if (part_num < 0) { 70090d27e7fSJason Zhu fb_add_string(response, chars_left, 70190d27e7fSJason Zhu "partition not found", NULL); 70290d27e7fSJason Zhu ret = -1; 70390d27e7fSJason Zhu } else if (!strncmp(PARTITION_TYPE_STRINGS, cmd, 70490d27e7fSJason Zhu strlen(PARTITION_TYPE_STRINGS))) { 70590d27e7fSJason Zhu if (fb_get_fstype("mmc", part_num, &fs_type)) { 70690d27e7fSJason Zhu fb_add_string(response, chars_left, 70790d27e7fSJason Zhu (char *)part_info.type, NULL); 70890d27e7fSJason Zhu } else { 70990d27e7fSJason Zhu fb_add_string(response, chars_left, 71090d27e7fSJason Zhu fs_type, NULL); 71190d27e7fSJason Zhu } 71290d27e7fSJason Zhu } else if (!strncmp("partition-size", cmd, 14)) { 71390d27e7fSJason Zhu u64 part_size; 71490d27e7fSJason Zhu 71590d27e7fSJason Zhu part_size = (uint64_t)part_info.size; 71690d27e7fSJason Zhu snprintf(response, chars_left, "0x%llx", 71790d27e7fSJason Zhu part_size * dev_desc->blksz); 71890d27e7fSJason Zhu } 71990d27e7fSJason Zhu #else 72090d27e7fSJason Zhu fb_add_string(response, chars_left, "not implemented", NULL); 72190d27e7fSJason Zhu ret = -1; 72290d27e7fSJason Zhu #endif 72390d27e7fSJason Zhu break; 72490d27e7fSJason Zhu } 72590d27e7fSJason Zhu case FB_BLK_SIZE: { 72690d27e7fSJason Zhu #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV 72790d27e7fSJason Zhu struct blk_desc *dev_desc; 72890d27e7fSJason Zhu 72990d27e7fSJason Zhu #ifdef CONFIG_RKIMG_BOOTLOADER 73090d27e7fSJason Zhu dev_desc = rockchip_get_bootdev(); 73190d27e7fSJason Zhu #else 73290d27e7fSJason Zhu dev_desc = NULL; 73390d27e7fSJason Zhu #endif 73490d27e7fSJason Zhu if (!dev_desc) { 73590d27e7fSJason Zhu fb_add_string(response, chars_left, 73690d27e7fSJason Zhu "block device not found", NULL); 73790d27e7fSJason Zhu ret = -1; 73890d27e7fSJason Zhu } else { 73990d27e7fSJason Zhu fb_add_number(response, chars_left, 74090d27e7fSJason Zhu "0x%lx", dev_desc->blksz); 74190d27e7fSJason Zhu } 74290d27e7fSJason Zhu #else 74390d27e7fSJason Zhu fb_add_string(response, chars_left, "not implemented", NULL); 74490d27e7fSJason Zhu ret = -1; 74590d27e7fSJason Zhu #endif 74690d27e7fSJason Zhu break; 74790d27e7fSJason Zhu } 74890d27e7fSJason Zhu case FB_ERASE_SIZE: { 74990d27e7fSJason Zhu #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV 75090d27e7fSJason Zhu lbaint_t erase_grp_size; 75190d27e7fSJason Zhu 75290d27e7fSJason Zhu erase_grp_size = fb_mmc_get_erase_grp_size(); 75390d27e7fSJason Zhu if (erase_grp_size < 0) { 75490d27e7fSJason Zhu fb_add_string(response, chars_left, 75590d27e7fSJason Zhu "block device not found", NULL); 75690d27e7fSJason Zhu ret = -1; 75790d27e7fSJason Zhu } else { 75890d27e7fSJason Zhu fb_add_number(response, chars_left, "0x"LBAF"", 75990d27e7fSJason Zhu erase_grp_size); 76090d27e7fSJason Zhu } 76190d27e7fSJason Zhu #else 76290d27e7fSJason Zhu fb_add_string(response, chars_left, "not implemented", NULL); 76390d27e7fSJason Zhu ret = -1; 76490d27e7fSJason Zhu #endif 76590d27e7fSJason Zhu break; 76690d27e7fSJason Zhu } 76790d27e7fSJason Zhu case FB_UNLOCKED: { 76837a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER 76990d27e7fSJason Zhu uint8_t flash_lock_state = 0; 77090d27e7fSJason Zhu 77190d27e7fSJason Zhu if (rk_avb_read_flash_lock_state(&flash_lock_state)) 77290d27e7fSJason Zhu fb_add_string(response, chars_left, "yes", NULL); 77390d27e7fSJason Zhu else 77490d27e7fSJason Zhu fb_add_string(response, chars_left, "no", NULL); 77590d27e7fSJason Zhu #else 77690d27e7fSJason Zhu fb_add_string(response, chars_left, "not implemented", NULL); 77790d27e7fSJason Zhu ret = -1; 77890d27e7fSJason Zhu #endif 77990d27e7fSJason Zhu break; 78090d27e7fSJason Zhu } 78190d27e7fSJason Zhu case FB_OFF_MODE_CHARGE: { 78290d27e7fSJason Zhu fb_add_string(response, chars_left, "not implemented", NULL); 78390d27e7fSJason Zhu break; 78490d27e7fSJason Zhu } 78590d27e7fSJason Zhu case FB_BATT_VOLTAGE: { 78690d27e7fSJason Zhu fb_add_string(response, chars_left, "not implemented", NULL); 78790d27e7fSJason Zhu break; 78890d27e7fSJason Zhu } 78990d27e7fSJason Zhu case FB_BATT_SOC_OK: { 79090d27e7fSJason Zhu fb_add_string(response, chars_left, "no", NULL); 79190d27e7fSJason Zhu break; 79290d27e7fSJason Zhu } 79390d27e7fSJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER 79490d27e7fSJason Zhu case FB_HAS_COUNT: { 795367cce4dSXu Hongfei char slot_count[2]; 796367cce4dSXu Hongfei char temp; 797367cce4dSXu Hongfei 798367cce4dSXu Hongfei slot_count[1] = '\0'; 79990d27e7fSJason Zhu if (rk_avb_read_slot_count(&temp) < 0) { 80090d27e7fSJason Zhu fb_add_number(response, chars_left, "%d", 0); 80190d27e7fSJason Zhu ret = -1; 80290d27e7fSJason Zhu break; 80390d27e7fSJason Zhu } 804367cce4dSXu Hongfei slot_count[0] = temp + 0x30; 80590d27e7fSJason Zhu fb_add_string(response, chars_left, slot_count, NULL); 80690d27e7fSJason Zhu break; 80790d27e7fSJason Zhu } 80890d27e7fSJason Zhu case FB_HAS_SLOT: { 80990d27e7fSJason Zhu char *part_name = cmd; 81090d27e7fSJason Zhu int has_slot = -1; 81190d27e7fSJason Zhu 81290d27e7fSJason Zhu cmd = strsep(&part_name, ":"); 81390d27e7fSJason Zhu if (!cmd || !part_name) { 81490d27e7fSJason Zhu fb_add_string(response, chars_left, 81590d27e7fSJason Zhu "argument Invalid!", NULL); 81690d27e7fSJason Zhu ret = -1; 81790d27e7fSJason Zhu break; 81890d27e7fSJason Zhu } 81990d27e7fSJason Zhu 82090d27e7fSJason Zhu has_slot = rk_avb_get_part_has_slot_info(part_name); 82190d27e7fSJason Zhu if (has_slot < 0) 82290d27e7fSJason Zhu fb_add_string(response, chars_left, "no", NULL); 82390d27e7fSJason Zhu else 82490d27e7fSJason Zhu fb_add_string(response, chars_left, "yes", NULL); 82590d27e7fSJason Zhu break; 82690d27e7fSJason Zhu } 82790d27e7fSJason Zhu case FB_CURR_SLOT: { 828367cce4dSXu Hongfei char slot_surrent[8] = {0}; 829367cce4dSXu Hongfei 83090d27e7fSJason Zhu if (!rk_avb_get_current_slot(slot_surrent)) { 83190d27e7fSJason Zhu fb_add_string(response, chars_left, 83290d27e7fSJason Zhu slot_surrent + 1, NULL); 83390d27e7fSJason Zhu } else { 83490d27e7fSJason Zhu fb_add_string(response, chars_left, "get error", NULL); 83590d27e7fSJason Zhu ret = -1; 83690d27e7fSJason Zhu } 83790d27e7fSJason Zhu break; 83890d27e7fSJason Zhu } 83990d27e7fSJason Zhu case FB_SLOT_SUFFIXES: { 84090d27e7fSJason Zhu char slot_suffixes_temp[4] = {0}; 84190d27e7fSJason Zhu char slot_suffixes[9] = {0}; 842367cce4dSXu Hongfei int slot_cnt = 0; 843367cce4dSXu Hongfei 84437a7bc39SJason Zhu rk_avb_read_slot_suffixes(slot_suffixes_temp); 845367cce4dSXu Hongfei while (slot_suffixes_temp[slot_cnt] != '\0') { 846367cce4dSXu Hongfei slot_suffixes[slot_cnt * 2] 847367cce4dSXu Hongfei = slot_suffixes_temp[slot_cnt]; 848367cce4dSXu Hongfei slot_suffixes[slot_cnt * 2 + 1] = ','; 849367cce4dSXu Hongfei slot_cnt++; 850367cce4dSXu Hongfei } 851367cce4dSXu Hongfei 85290d27e7fSJason Zhu slot_suffixes[(slot_cnt - 1) * 2 + 1] = '\0'; 85390d27e7fSJason Zhu fb_add_string(response, chars_left, slot_suffixes, NULL); 85490d27e7fSJason Zhu break; 855367cce4dSXu Hongfei } 85690d27e7fSJason Zhu case FB_SLOT_SUCCESSFUL:{ 857374a9995SCody Xie char *slot_name = cmd; 85890d27e7fSJason Zhu AvbABData ab_info; 859374a9995SCody Xie 860374a9995SCody Xie cmd = strsep(&slot_name, ":"); 86190d27e7fSJason Zhu if (!cmd || !slot_name) { 86290d27e7fSJason Zhu fb_add_string(response, chars_left, 86390d27e7fSJason Zhu "argument Invalid!", NULL); 86490d27e7fSJason Zhu ret = -1; 86590d27e7fSJason Zhu break; 866374a9995SCody Xie } 867374a9995SCody Xie 86890d27e7fSJason Zhu if (rk_avb_get_ab_info(&ab_info) < 0) { 86990d27e7fSJason Zhu fb_add_string(response, chars_left, 87090d27e7fSJason Zhu "get ab info failed!", NULL); 87190d27e7fSJason Zhu ret = -1; 87290d27e7fSJason Zhu break; 873374a9995SCody Xie } 874374a9995SCody Xie 875374a9995SCody Xie if (!strcmp(slot_name, "a")) { 87690d27e7fSJason Zhu if (ab_info.slots[0].successful_boot) 87790d27e7fSJason Zhu fb_add_string(response, chars_left, 87890d27e7fSJason Zhu "yes", NULL); 87990d27e7fSJason Zhu else 88090d27e7fSJason Zhu fb_add_string(response, chars_left, 88190d27e7fSJason Zhu "no", NULL); 882374a9995SCody Xie } else if (!strcmp(slot_name, "b")) { 88390d27e7fSJason Zhu if (ab_info.slots[1].successful_boot) 88490d27e7fSJason Zhu fb_add_string(response, chars_left, 88590d27e7fSJason Zhu "yes", NULL); 88690d27e7fSJason Zhu else 88790d27e7fSJason Zhu fb_add_string(response, chars_left, 88890d27e7fSJason Zhu "no", NULL); 88990d27e7fSJason Zhu } else { 89090d27e7fSJason Zhu fb_add_string(response, chars_left, "no", NULL); 89190d27e7fSJason Zhu } 89290d27e7fSJason Zhu break; 89390d27e7fSJason Zhu } 89490d27e7fSJason Zhu case FB_SLOT_UNBOOTABLE: { 89590d27e7fSJason Zhu char *slot_name = cmd; 89690d27e7fSJason Zhu AvbABData ab_info; 89790d27e7fSJason Zhu 89890d27e7fSJason Zhu cmd = strsep(&slot_name, ":"); 89990d27e7fSJason Zhu 90090d27e7fSJason Zhu if (!cmd || !slot_name) { 90190d27e7fSJason Zhu fb_add_string(response, chars_left, 90290d27e7fSJason Zhu "argument Invalid!", NULL); 90390d27e7fSJason Zhu ret = -1; 90490d27e7fSJason Zhu break; 90590d27e7fSJason Zhu } 90690d27e7fSJason Zhu 90790d27e7fSJason Zhu if (rk_avb_get_ab_info(&ab_info) < 0) { 90890d27e7fSJason Zhu fb_add_string(response, chars_left, 90990d27e7fSJason Zhu "get ab info failed!", NULL); 91090d27e7fSJason Zhu ret = -1; 91190d27e7fSJason Zhu break; 91290d27e7fSJason Zhu } 91390d27e7fSJason Zhu 91490d27e7fSJason Zhu if (!strcmp(slot_name, "a")) { 91590d27e7fSJason Zhu if (!ab_info.slots[0].successful_boot && 91690d27e7fSJason Zhu !ab_info.slots[0].tries_remaining && 91790d27e7fSJason Zhu !ab_info.slots[0].priority) 91890d27e7fSJason Zhu fb_add_string(response, chars_left, 91990d27e7fSJason Zhu "yes", NULL); 92090d27e7fSJason Zhu else 92190d27e7fSJason Zhu fb_add_string(response, chars_left, "no", NULL); 92290d27e7fSJason Zhu } else if (!strcmp(slot_name, "b")) { 92390d27e7fSJason Zhu if (!ab_info.slots[1].successful_boot && 92490d27e7fSJason Zhu !ab_info.slots[1].tries_remaining && 92590d27e7fSJason Zhu !ab_info.slots[1].priority) 92690d27e7fSJason Zhu fb_add_string(response, chars_left, 92790d27e7fSJason Zhu "yes", NULL); 92890d27e7fSJason Zhu else 92990d27e7fSJason Zhu fb_add_string(response, chars_left, "no", NULL); 93090d27e7fSJason Zhu } else { 93190d27e7fSJason Zhu fb_add_string(response, chars_left, "no", NULL); 93290d27e7fSJason Zhu } 93390d27e7fSJason Zhu break; 93490d27e7fSJason Zhu } 93590d27e7fSJason Zhu case FB_SLOT_RETRY_COUNT: { 93690d27e7fSJason Zhu char *slot_name = cmd; 93790d27e7fSJason Zhu AvbABData ab_info; 93890d27e7fSJason Zhu 93990d27e7fSJason Zhu cmd = strsep(&slot_name, ":"); 94090d27e7fSJason Zhu if (!cmd || !slot_name) { 94190d27e7fSJason Zhu fb_add_string(response, chars_left, 94290d27e7fSJason Zhu "argument Invalid!", NULL); 94390d27e7fSJason Zhu ret = -1; 94490d27e7fSJason Zhu break; 94590d27e7fSJason Zhu } 94690d27e7fSJason Zhu 94790d27e7fSJason Zhu if (rk_avb_get_ab_info(&ab_info) < 0) { 94890d27e7fSJason Zhu fb_add_string(response, chars_left, 94990d27e7fSJason Zhu "get ab info failed!", NULL); 95090d27e7fSJason Zhu ret = -1; 95190d27e7fSJason Zhu break; 95290d27e7fSJason Zhu } 95390d27e7fSJason Zhu 95490d27e7fSJason Zhu if (!strcmp(slot_name, "a")) { 95590d27e7fSJason Zhu fb_add_number(response, chars_left, 95690d27e7fSJason Zhu "%d", ab_info.slots[0].tries_remaining); 95790d27e7fSJason Zhu } else if (!strcmp(slot_name, "b")) { 95890d27e7fSJason Zhu fb_add_number(response, chars_left, "%d", 95990d27e7fSJason Zhu ab_info.slots[1].tries_remaining); 96090d27e7fSJason Zhu 961374a9995SCody Xie } else { 962374a9995SCody Xie strcpy(response, "FAILno"); 963374a9995SCody Xie } 96490d27e7fSJason Zhu break; 965367cce4dSXu Hongfei } 96690d27e7fSJason Zhu case FB_AT_VBST: { 96790d27e7fSJason Zhu char vbst[VBOOT_STATE_SIZE] = {0}; 96890d27e7fSJason Zhu char *p_vbst; 969f61a997eSqiujian 97090d27e7fSJason Zhu strcpy(response, "INFO"); 97190d27e7fSJason Zhu rk_avb_get_at_vboot_state(vbst); 97290d27e7fSJason Zhu p_vbst = vbst; 97390d27e7fSJason Zhu do { 97490d27e7fSJason Zhu cmd = strsep(&p_vbst, "\n"); 97590d27e7fSJason Zhu if (strlen(cmd) > 0) { 97690d27e7fSJason Zhu memcpy(&response[4], cmd, chars_left); 97790d27e7fSJason Zhu fastboot_tx_write_str(response); 978f61a997eSqiujian } 97990d27e7fSJason Zhu } while (strlen(cmd)); 98090d27e7fSJason Zhu break; 98190d27e7fSJason Zhu } 98290d27e7fSJason Zhu #endif 98390d27e7fSJason Zhu #ifdef CONFIG_OPTEE_CLIENT 98490d27e7fSJason Zhu case FB_AT_DH: { 98590d27e7fSJason Zhu char dhbuf[ATTEST_DH_SIZE]; 98690d27e7fSJason Zhu uint32_t dh_len = ATTEST_DH_SIZE; 98790d27e7fSJason Zhu uint32_t res = trusty_attest_dh((uint8_t *)dhbuf, &dh_len); 988f61a997eSqiujian 98990d27e7fSJason Zhu if (res) { 99090d27e7fSJason Zhu fb_add_string(response, chars_left, "dh not set", NULL); 99190d27e7fSJason Zhu ret = -1; 9923aab70afSSebastian Siewior } else { 99390d27e7fSJason Zhu fb_add_string(response, chars_left, dhbuf, NULL); 99490d27e7fSJason Zhu } 99590d27e7fSJason Zhu break; 99690d27e7fSJason Zhu } 99790d27e7fSJason Zhu case FB_AT_UUID: { 99890d27e7fSJason Zhu char uuid[ATTEST_UUID_SIZE] = {0}; 99990d27e7fSJason Zhu uint32_t uuid_len = ATTEST_UUID_SIZE; 100090d27e7fSJason Zhu uint32_t res = trusty_attest_uuid((uint8_t *)uuid, &uuid_len); 100190d27e7fSJason Zhu 100290d27e7fSJason Zhu uuid[ATTEST_UUID_SIZE - 1] = 0; 100390d27e7fSJason Zhu if (res) { 100490d27e7fSJason Zhu fb_add_string(response, chars_left, "dh not set", NULL); 100590d27e7fSJason Zhu ret = -1; 100690d27e7fSJason Zhu } else { 100790d27e7fSJason Zhu fb_add_string(response, chars_left, uuid, NULL); 100890d27e7fSJason Zhu } 100990d27e7fSJason Zhu break; 101090d27e7fSJason Zhu } 101190d27e7fSJason Zhu #endif 101290d27e7fSJason Zhu default: { 1013b1f2a17cSnicolas.le.bayon@st.com char *envstr; 101474322201SRob Herring 1015b1f2a17cSnicolas.le.bayon@st.com envstr = malloc(strlen("fastboot.") + strlen(cmd) + 1); 1016b1f2a17cSnicolas.le.bayon@st.com if (!envstr) { 101790d27e7fSJason Zhu fb_add_string(response, chars_left, 101890d27e7fSJason Zhu "malloc error", NULL); 101990d27e7fSJason Zhu ret = -1; 102090d27e7fSJason Zhu break; 1021b1f2a17cSnicolas.le.bayon@st.com } 1022b1f2a17cSnicolas.le.bayon@st.com 1023b1f2a17cSnicolas.le.bayon@st.com sprintf(envstr, "fastboot.%s", cmd); 102400caae6dSSimon Glass s = env_get(envstr); 102574322201SRob Herring if (s) { 102674322201SRob Herring strncat(response, s, chars_left); 102774322201SRob Herring } else { 1028a18c2706SSteve Rae printf("WARNING: unknown variable: %s\n", cmd); 102990d27e7fSJason Zhu fb_add_string(response, chars_left, 103090d27e7fSJason Zhu "not implemented", NULL); 10313aab70afSSebastian Siewior } 1032b1f2a17cSnicolas.le.bayon@st.com 1033b1f2a17cSnicolas.le.bayon@st.com free(envstr); 103490d27e7fSJason Zhu break; 103590d27e7fSJason Zhu } 103690d27e7fSJason Zhu } 103790d27e7fSJason Zhu 103890d27e7fSJason Zhu return ret; 103990d27e7fSJason Zhu } 104090d27e7fSJason Zhu 104190d27e7fSJason Zhu static const struct { 104290d27e7fSJason Zhu /* 104390d27e7fSJason Zhu *any changes to this array require an update to the corresponding 104490d27e7fSJason Zhu *enum in fastboot.h 104590d27e7fSJason Zhu */ 104690d27e7fSJason Zhu struct name_string name; 104790d27e7fSJason Zhu fb_getvar_t var; 104890d27e7fSJason Zhu } getvar_table[] = { 104990d27e7fSJason Zhu { NAME_NO_ARGS("version"), FB_VERSION}, 105090d27e7fSJason Zhu { NAME_NO_ARGS("version-bootloader"), FB_BOOTLOADER_VERSION}, 105190d27e7fSJason Zhu { NAME_NO_ARGS("version-baseband"), FB_BASEBAND_VERSION}, 105290d27e7fSJason Zhu { NAME_NO_ARGS("product"), FB_PRODUCT}, 105390d27e7fSJason Zhu { NAME_NO_ARGS("serialno"), FB_SERIAL_NO}, 105490d27e7fSJason Zhu { NAME_NO_ARGS("secure"), FB_SECURE}, 105590d27e7fSJason Zhu { NAME_NO_ARGS("max-download-size"), FB_DWNLD_SIZE}, 105690d27e7fSJason Zhu { NAME_NO_ARGS("logical-block-size"), FB_BLK_SIZE}, 105790d27e7fSJason Zhu { NAME_NO_ARGS("erase-block-size"), FB_ERASE_SIZE}, 105890d27e7fSJason Zhu { NAME_ARGS("partition-type", ':'), FB_PART_TYPE}, 105990d27e7fSJason Zhu { NAME_ARGS("partition-size", ':'), FB_PART_SIZE}, 106090d27e7fSJason Zhu { NAME_NO_ARGS("unlocked"), FB_UNLOCKED}, 106190d27e7fSJason Zhu { NAME_NO_ARGS("off-mode-charge"), FB_OFF_MODE_CHARGE}, 106290d27e7fSJason Zhu { NAME_NO_ARGS("battery-voltage"), FB_BATT_VOLTAGE}, 106390d27e7fSJason Zhu { NAME_NO_ARGS("variant"), FB_VARIANT}, 106490d27e7fSJason Zhu { NAME_NO_ARGS("battery-soc-ok"), FB_BATT_SOC_OK}, 106590d27e7fSJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER 106690d27e7fSJason Zhu /* Slots related */ 106790d27e7fSJason Zhu { NAME_NO_ARGS("slot-count"), FB_HAS_COUNT}, 106890d27e7fSJason Zhu { NAME_ARGS("has-slot", ':'), FB_HAS_SLOT}, 106990d27e7fSJason Zhu { NAME_NO_ARGS("current-slot"), FB_CURR_SLOT}, 107090d27e7fSJason Zhu { NAME_NO_ARGS("slot-suffixes"), FB_SLOT_SUFFIXES}, 107190d27e7fSJason Zhu { NAME_ARGS("slot-successful", ':'), FB_SLOT_SUCCESSFUL}, 107290d27e7fSJason Zhu { NAME_ARGS("slot-unbootable", ':'), FB_SLOT_UNBOOTABLE}, 107390d27e7fSJason Zhu { NAME_ARGS("slot-retry-count", ':'), FB_SLOT_RETRY_COUNT}, 107490d27e7fSJason Zhu { NAME_NO_ARGS("at-vboot-state"), FB_AT_VBST}, 107590d27e7fSJason Zhu #endif 107690d27e7fSJason Zhu /* 107790d27e7fSJason Zhu * OEM specific : 107890d27e7fSJason Zhu * Spec says names starting with lowercase letter are reserved. 107990d27e7fSJason Zhu */ 108090d27e7fSJason Zhu #ifdef CONFIG_OPTEE_CLIENT 108190d27e7fSJason Zhu { NAME_NO_ARGS("at-attest-dh"), FB_AT_DH}, 108290d27e7fSJason Zhu { NAME_NO_ARGS("at-attest-uuid"), FB_AT_UUID}, 108390d27e7fSJason Zhu #endif 108490d27e7fSJason Zhu }; 108590d27e7fSJason Zhu 108690d27e7fSJason Zhu static int fb_getvar_single(char *cmd, char *response, size_t chars_left) 108790d27e7fSJason Zhu { 108890d27e7fSJason Zhu int i; 108990d27e7fSJason Zhu size_t match_len = 0; 109090d27e7fSJason Zhu size_t len = strlen(cmd); 109190d27e7fSJason Zhu 109290d27e7fSJason Zhu for (i = 0; i < ARRAY_SIZE(getvar_table); i++) { 109390d27e7fSJason Zhu match_len = name_check_match(cmd, len, &getvar_table[i].name); 109490d27e7fSJason Zhu if (match_len) 109590d27e7fSJason Zhu break; 109690d27e7fSJason Zhu } 109790d27e7fSJason Zhu 109890d27e7fSJason Zhu if (match_len == 0) { 109990d27e7fSJason Zhu fb_add_string(response, chars_left, "unknown variable", NULL); 110090d27e7fSJason Zhu return -1; 110190d27e7fSJason Zhu } 110290d27e7fSJason Zhu 110390d27e7fSJason Zhu if (fb_read_var(cmd, response, getvar_table[i].var, chars_left) < 0) 110490d27e7fSJason Zhu return -1; 110590d27e7fSJason Zhu 110690d27e7fSJason Zhu return 0; 110790d27e7fSJason Zhu } 110890d27e7fSJason Zhu 110990d27e7fSJason Zhu static void fb_getvar_all(void) 111090d27e7fSJason Zhu { 111190d27e7fSJason Zhu char response[FASTBOOT_RESPONSE_LEN] = {0}; 111290d27e7fSJason Zhu char resp_tmp[FASTBOOT_RESPONSE_LEN] = {0}; 111390d27e7fSJason Zhu char *actual_resp; 111490d27e7fSJason Zhu size_t chars_left; 111590d27e7fSJason Zhu int i, p; 111690d27e7fSJason Zhu disk_partition_t part_info; 111790d27e7fSJason Zhu struct blk_desc *dev_desc; 111890d27e7fSJason Zhu 111990d27e7fSJason Zhu strcpy(response, "INFO"); 112090d27e7fSJason Zhu chars_left = sizeof(response) - strlen(response) - 1; 112190d27e7fSJason Zhu actual_resp = response + strlen(response); 112290d27e7fSJason Zhu 112390d27e7fSJason Zhu for (i = 0; i < ARRAY_SIZE(getvar_table); i++) { 112490d27e7fSJason Zhu fb_getvar_t var = getvar_table[i].var; 112590d27e7fSJason Zhu 112690d27e7fSJason Zhu switch (var) { 112790d27e7fSJason Zhu case FB_PART_TYPE: 112890d27e7fSJason Zhu case FB_PART_SIZE: { 112990d27e7fSJason Zhu const char *fs_type = NULL; 113090d27e7fSJason Zhu #ifdef CONFIG_RKIMG_BOOTLOADER 113190d27e7fSJason Zhu dev_desc = rockchip_get_bootdev(); 113290d27e7fSJason Zhu #else 113390d27e7fSJason Zhu dev_desc = NULL; 113490d27e7fSJason Zhu #endif 113590d27e7fSJason Zhu if (!dev_desc) { 113690d27e7fSJason Zhu fb_add_string(actual_resp, chars_left, 113790d27e7fSJason Zhu "%s:block device not found", 113890d27e7fSJason Zhu getvar_table[i].name.str); 113990d27e7fSJason Zhu fastboot_tx_write_str(response); 114090d27e7fSJason Zhu break; 114190d27e7fSJason Zhu } 114290d27e7fSJason Zhu 114390d27e7fSJason Zhu for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { 114490d27e7fSJason Zhu if (part_get_info(dev_desc, p, 114590d27e7fSJason Zhu &part_info) < 0) { 114690d27e7fSJason Zhu break; 114790d27e7fSJason Zhu } 114890d27e7fSJason Zhu 114990d27e7fSJason Zhu if (var == FB_PART_TYPE) { 115090d27e7fSJason Zhu fs_type = NULL; 115190d27e7fSJason Zhu if (fb_get_fstype("mmc", p, 115290d27e7fSJason Zhu &fs_type)) { 115390d27e7fSJason Zhu fb_add_string( 115490d27e7fSJason Zhu resp_tmp, 115590d27e7fSJason Zhu FASTBOOT_RESPONSE_LEN, 115690d27e7fSJason Zhu (char *)part_info.type, 115790d27e7fSJason Zhu NULL); 115890d27e7fSJason Zhu } else { 115990d27e7fSJason Zhu fb_add_string( 116090d27e7fSJason Zhu resp_tmp, 116190d27e7fSJason Zhu FASTBOOT_RESPONSE_LEN, 116290d27e7fSJason Zhu fs_type, 116390d27e7fSJason Zhu NULL); 116490d27e7fSJason Zhu } 116590d27e7fSJason Zhu 116690d27e7fSJason Zhu snprintf(actual_resp, 116790d27e7fSJason Zhu chars_left, 116890d27e7fSJason Zhu "%s:%s:%s", 116990d27e7fSJason Zhu getvar_table[i].name.str, 117090d27e7fSJason Zhu part_info.name, 117190d27e7fSJason Zhu resp_tmp); 117290d27e7fSJason Zhu } else { 117390d27e7fSJason Zhu uint64_t part_size; 117490d27e7fSJason Zhu 117590d27e7fSJason Zhu part_size = (uint64_t)part_info.size; 117690d27e7fSJason Zhu snprintf(actual_resp, 117790d27e7fSJason Zhu chars_left, 117890d27e7fSJason Zhu "%s:%s:0x%llx", 117990d27e7fSJason Zhu getvar_table[i].name.str, 118090d27e7fSJason Zhu part_info.name, 118190d27e7fSJason Zhu part_size * dev_desc->blksz); 118274322201SRob Herring } 11833aab70afSSebastian Siewior fastboot_tx_write_str(response); 11843aab70afSSebastian Siewior } 118590d27e7fSJason Zhu break; 118690d27e7fSJason Zhu } 118790d27e7fSJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER 118890d27e7fSJason Zhu case FB_HAS_SLOT: { 118990d27e7fSJason Zhu uchar *ptr_name_tmp; 119090d27e7fSJason Zhu char c = '_'; 119190d27e7fSJason Zhu int has_slot = -1; 119290d27e7fSJason Zhu 119390d27e7fSJason Zhu #ifdef CONFIG_RKIMG_BOOTLOADER 119490d27e7fSJason Zhu dev_desc = rockchip_get_bootdev(); 119590d27e7fSJason Zhu #else 119690d27e7fSJason Zhu dev_desc = NULL; 119790d27e7fSJason Zhu #endif 119890d27e7fSJason Zhu if (!dev_desc) { 119990d27e7fSJason Zhu fb_add_string(actual_resp, chars_left, 120090d27e7fSJason Zhu "%s:block device not found", 120190d27e7fSJason Zhu getvar_table[i].name.str); 120290d27e7fSJason Zhu fastboot_tx_write_str(response); 120390d27e7fSJason Zhu break; 120490d27e7fSJason Zhu } 120590d27e7fSJason Zhu 120690d27e7fSJason Zhu for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { 120790d27e7fSJason Zhu if (part_get_info(dev_desc, p, 120890d27e7fSJason Zhu &part_info) < 0) { 120990d27e7fSJason Zhu break; 121090d27e7fSJason Zhu } else { 121190d27e7fSJason Zhu ptr_name_tmp = (uchar *)strrchr( 121290d27e7fSJason Zhu (char *)part_info.name, c); 121390d27e7fSJason Zhu if (ptr_name_tmp && 121490d27e7fSJason Zhu part_info.name[ptr_name_tmp - 121590d27e7fSJason Zhu part_info.name + 2] == '\0') 121690d27e7fSJason Zhu fb_add_string( 121790d27e7fSJason Zhu resp_tmp, 121890d27e7fSJason Zhu ptr_name_tmp - 121990d27e7fSJason Zhu part_info.name + 1, 122090d27e7fSJason Zhu (char *)part_info.name, 122190d27e7fSJason Zhu NULL); 122290d27e7fSJason Zhu else 122390d27e7fSJason Zhu strcpy(resp_tmp, 122490d27e7fSJason Zhu (char *)part_info.name); 122590d27e7fSJason Zhu 122690d27e7fSJason Zhu has_slot = rk_avb_get_part_has_slot_info( 122790d27e7fSJason Zhu resp_tmp); 122890d27e7fSJason Zhu if (has_slot < 0) { 122990d27e7fSJason Zhu snprintf(actual_resp, 123090d27e7fSJason Zhu chars_left, 123190d27e7fSJason Zhu "%s:%s:no", 123290d27e7fSJason Zhu getvar_table[i].name.str, 123390d27e7fSJason Zhu resp_tmp); 123490d27e7fSJason Zhu } else { 123590d27e7fSJason Zhu snprintf(actual_resp, 123690d27e7fSJason Zhu chars_left, 123790d27e7fSJason Zhu "%s:%s:yes", 123890d27e7fSJason Zhu getvar_table[i].name.str, 123990d27e7fSJason Zhu resp_tmp); 124090d27e7fSJason Zhu p++; 124190d27e7fSJason Zhu } 124290d27e7fSJason Zhu 124390d27e7fSJason Zhu fastboot_tx_write_str(response); 124490d27e7fSJason Zhu } 124590d27e7fSJason Zhu } 124690d27e7fSJason Zhu break; 124790d27e7fSJason Zhu } 124890d27e7fSJason Zhu 124990d27e7fSJason Zhu case FB_SLOT_SUCCESSFUL: { 125090d27e7fSJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER 125190d27e7fSJason Zhu AvbABData ab_info; 125290d27e7fSJason Zhu 125390d27e7fSJason Zhu if (rk_avb_get_ab_info(&ab_info) < 0) { 125490d27e7fSJason Zhu fb_add_string(actual_resp, 125590d27e7fSJason Zhu chars_left, 125690d27e7fSJason Zhu "%s:get ab info failed!", 125790d27e7fSJason Zhu getvar_table[i].name.str); 125890d27e7fSJason Zhu fastboot_tx_write_str(response); 125990d27e7fSJason Zhu break; 126090d27e7fSJason Zhu } 126190d27e7fSJason Zhu 126290d27e7fSJason Zhu if (ab_info.slots[0].successful_boot) 126390d27e7fSJason Zhu fb_add_string(actual_resp, chars_left, 126490d27e7fSJason Zhu "%s:a:yes", 126590d27e7fSJason Zhu getvar_table[i].name.str); 126690d27e7fSJason Zhu else 126790d27e7fSJason Zhu fb_add_string(actual_resp, chars_left, 126890d27e7fSJason Zhu "%s:a:no", 126990d27e7fSJason Zhu getvar_table[i].name.str); 127090d27e7fSJason Zhu fastboot_tx_write_str(response); 127190d27e7fSJason Zhu 127290d27e7fSJason Zhu if (ab_info.slots[1].successful_boot) 127390d27e7fSJason Zhu fb_add_string(actual_resp, chars_left, 127490d27e7fSJason Zhu "%s:b:yes", 127590d27e7fSJason Zhu getvar_table[i].name.str); 127690d27e7fSJason Zhu else 127790d27e7fSJason Zhu fb_add_string(actual_resp, chars_left, 127890d27e7fSJason Zhu "%s:b:no", 127990d27e7fSJason Zhu getvar_table[i].name.str); 128090d27e7fSJason Zhu fastboot_tx_write_str(response); 128190d27e7fSJason Zhu #else 128290d27e7fSJason Zhu fb_add_string(actual_resp, chars_left, 128390d27e7fSJason Zhu "%s:not find ab info!", 128490d27e7fSJason Zhu getvar_table[i].name.str); 128590d27e7fSJason Zhu fastboot_tx_write_str(response); 128690d27e7fSJason Zhu #endif 128790d27e7fSJason Zhu break; 128890d27e7fSJason Zhu } 128990d27e7fSJason Zhu 129090d27e7fSJason Zhu case FB_SLOT_UNBOOTABLE: { 129190d27e7fSJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER 129290d27e7fSJason Zhu AvbABData ab_info; 129390d27e7fSJason Zhu 129490d27e7fSJason Zhu if (rk_avb_get_ab_info(&ab_info) < 0) { 129590d27e7fSJason Zhu fb_add_string(actual_resp, chars_left, 129690d27e7fSJason Zhu "%s:not find ab info!", 129790d27e7fSJason Zhu getvar_table[i].name.str); 129890d27e7fSJason Zhu fastboot_tx_write_str(response); 129990d27e7fSJason Zhu break; 130090d27e7fSJason Zhu } 130190d27e7fSJason Zhu 130290d27e7fSJason Zhu if (!ab_info.slots[0].successful_boot && 130390d27e7fSJason Zhu !ab_info.slots[0].tries_remaining && 130490d27e7fSJason Zhu !ab_info.slots[0].priority) 130590d27e7fSJason Zhu fb_add_string(actual_resp, chars_left, 130690d27e7fSJason Zhu "%s:a:yes", 130790d27e7fSJason Zhu getvar_table[i].name.str); 130890d27e7fSJason Zhu else 130990d27e7fSJason Zhu fb_add_string(actual_resp, chars_left, 131090d27e7fSJason Zhu "%s:a:no", 131190d27e7fSJason Zhu getvar_table[i].name.str); 131290d27e7fSJason Zhu fastboot_tx_write_str(response); 131390d27e7fSJason Zhu 131490d27e7fSJason Zhu if (!ab_info.slots[1].successful_boot && 131590d27e7fSJason Zhu !ab_info.slots[1].tries_remaining && 131690d27e7fSJason Zhu !ab_info.slots[1].priority) 131790d27e7fSJason Zhu fb_add_string(actual_resp, chars_left, 131890d27e7fSJason Zhu "%s:b:yes", 131990d27e7fSJason Zhu getvar_table[i].name.str); 132090d27e7fSJason Zhu else 132190d27e7fSJason Zhu fb_add_string(actual_resp, chars_left, 132290d27e7fSJason Zhu "%s:b:no", 132390d27e7fSJason Zhu getvar_table[i].name.str); 132490d27e7fSJason Zhu 132590d27e7fSJason Zhu fastboot_tx_write_str(response); 132690d27e7fSJason Zhu #else 132790d27e7fSJason Zhu fb_add_string(actual_resp, chars_left, 132890d27e7fSJason Zhu "%s:not find ab info!", 132990d27e7fSJason Zhu getvar_table[i].name.str); 133090d27e7fSJason Zhu fastboot_tx_write_str(response); 133190d27e7fSJason Zhu #endif 133290d27e7fSJason Zhu break; 133390d27e7fSJason Zhu } 133490d27e7fSJason Zhu 133590d27e7fSJason Zhu case FB_SLOT_RETRY_COUNT: { 133690d27e7fSJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER 133790d27e7fSJason Zhu AvbABData ab_info; 133890d27e7fSJason Zhu 133990d27e7fSJason Zhu if (rk_avb_get_ab_info(&ab_info) < 0) { 134090d27e7fSJason Zhu fb_add_string(actual_resp, chars_left, 134190d27e7fSJason Zhu "%s:not find ab info!", 134290d27e7fSJason Zhu getvar_table[i].name.str); 134390d27e7fSJason Zhu fastboot_tx_write_str(response); 134490d27e7fSJason Zhu break; 134590d27e7fSJason Zhu } 134690d27e7fSJason Zhu 134790d27e7fSJason Zhu snprintf(actual_resp, chars_left, "%s:a:%d", 134890d27e7fSJason Zhu getvar_table[i].name.str, 134990d27e7fSJason Zhu ab_info.slots[1].tries_remaining); 135090d27e7fSJason Zhu fastboot_tx_write_str(response); 135190d27e7fSJason Zhu snprintf(actual_resp, chars_left, "%s:b:%d", 135290d27e7fSJason Zhu getvar_table[i].name.str, 135390d27e7fSJason Zhu ab_info.slots[1].tries_remaining); 135490d27e7fSJason Zhu fastboot_tx_write_str(response); 135590d27e7fSJason Zhu #else 135690d27e7fSJason Zhu fb_add_string(actual_resp, chars_left, 135790d27e7fSJason Zhu "%s:not find ab info!", 135890d27e7fSJason Zhu getvar_table[i].name.str); 135990d27e7fSJason Zhu fastboot_tx_write_str(response); 136090d27e7fSJason Zhu #endif 136190d27e7fSJason Zhu break; 136290d27e7fSJason Zhu } 136390d27e7fSJason Zhu #endif 136490d27e7fSJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER 136590d27e7fSJason Zhu case FB_AT_VBST: 136690d27e7fSJason Zhu break; 136790d27e7fSJason Zhu #endif 136890d27e7fSJason Zhu default: 136990d27e7fSJason Zhu fb_getvar_single((char *)getvar_table[i].name.str, 137090d27e7fSJason Zhu resp_tmp, FASTBOOT_RESPONSE_LEN); 137190d27e7fSJason Zhu snprintf(actual_resp, chars_left, "%s:%s", 137290d27e7fSJason Zhu getvar_table[i].name.str, resp_tmp); 137390d27e7fSJason Zhu fastboot_tx_write_str(response); 137490d27e7fSJason Zhu } 137590d27e7fSJason Zhu } 137690d27e7fSJason Zhu } 137790d27e7fSJason Zhu 137890d27e7fSJason Zhu static void cb_getvar(struct usb_ep *ep, struct usb_request *req) 137990d27e7fSJason Zhu { 138090d27e7fSJason Zhu char *cmd = req->buf; 138190d27e7fSJason Zhu char response[FASTBOOT_RESPONSE_LEN] = {0}; 138290d27e7fSJason Zhu const char *str_read_all = "all"; 138390d27e7fSJason Zhu size_t len = 0; 138490d27e7fSJason Zhu size_t chars_left; 138590d27e7fSJason Zhu 138690d27e7fSJason Zhu strsep(&cmd, ":"); 138790d27e7fSJason Zhu if (!cmd) { 138890d27e7fSJason Zhu pr_err("missing variable"); 138990d27e7fSJason Zhu fastboot_tx_write_str("FAILmissing var"); 139090d27e7fSJason Zhu return; 139190d27e7fSJason Zhu } 139290d27e7fSJason Zhu 139390d27e7fSJason Zhu len = strlen(cmd); 139490d27e7fSJason Zhu if (len == strlen(str_read_all) && 139590d27e7fSJason Zhu (strncmp(cmd, str_read_all, len) == 0)) { 139690d27e7fSJason Zhu fb_getvar_all(); 139790d27e7fSJason Zhu fastboot_tx_write_str("OKAYDone!"); 139890d27e7fSJason Zhu } else { 139990d27e7fSJason Zhu strcpy(response, "OKAY"); 140090d27e7fSJason Zhu chars_left = sizeof(response) - strlen(response) - 1; 140190d27e7fSJason Zhu 140290d27e7fSJason Zhu if (fb_getvar_single(cmd, &response[strlen(response)], 140390d27e7fSJason Zhu chars_left) < 0) { 140490d27e7fSJason Zhu strcpy(cmd, "FAILunknown variable"); 140590d27e7fSJason Zhu strncat(cmd, &response[strlen(response)], chars_left); 140690d27e7fSJason Zhu fastboot_tx_write_str(cmd); 140790d27e7fSJason Zhu return; 140890d27e7fSJason Zhu } 140990d27e7fSJason Zhu fastboot_tx_write_str(response); 141090d27e7fSJason Zhu } 141190d27e7fSJason Zhu 141290d27e7fSJason Zhu return; 141390d27e7fSJason Zhu } 14143aab70afSSebastian Siewior 1415ac484c5aSRoger Quadros static unsigned int rx_bytes_expected(struct usb_ep *ep) 14163aab70afSSebastian Siewior { 14173aab70afSSebastian Siewior int rx_remain = download_size - download_bytes; 1418ac484c5aSRoger Quadros unsigned int rem; 1419ac484c5aSRoger Quadros unsigned int maxpacket = ep->maxpacket; 1420ac484c5aSRoger Quadros 1421ac484c5aSRoger Quadros if (rx_remain <= 0) 14223aab70afSSebastian Siewior return 0; 1423ac484c5aSRoger Quadros else if (rx_remain > EP_BUFFER_SIZE) 14243aab70afSSebastian Siewior return EP_BUFFER_SIZE; 1425ac484c5aSRoger Quadros 1426ac484c5aSRoger Quadros /* 1427ac484c5aSRoger Quadros * Some controllers e.g. DWC3 don't like OUT transfers to be 1428ac484c5aSRoger Quadros * not ending in maxpacket boundary. So just make them happy by 1429ac484c5aSRoger Quadros * always requesting for integral multiple of maxpackets. 1430ac484c5aSRoger Quadros * This shouldn't bother controllers that don't care about it. 1431ac484c5aSRoger Quadros */ 14329e4b510dSDileep Katta rem = rx_remain % maxpacket; 1433ac484c5aSRoger Quadros if (rem > 0) 14349e4b510dSDileep Katta rx_remain = rx_remain + (maxpacket - rem); 1435ac484c5aSRoger Quadros 14363aab70afSSebastian Siewior return rx_remain; 14373aab70afSSebastian Siewior } 14383aab70afSSebastian Siewior 14393aab70afSSebastian Siewior #define BYTES_PER_DOT 0x20000 14403aab70afSSebastian Siewior static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) 14413aab70afSSebastian Siewior { 14423c8f98f5SMaxime Ripard char response[FASTBOOT_RESPONSE_LEN]; 14433aab70afSSebastian Siewior unsigned int transfer_size = download_size - download_bytes; 14443aab70afSSebastian Siewior const unsigned char *buffer = req->buf; 14453aab70afSSebastian Siewior unsigned int buffer_size = req->actual; 144623d1d10cSBo Shen unsigned int pre_dot_num, now_dot_num; 14473aab70afSSebastian Siewior 14483aab70afSSebastian Siewior if (req->status != 0) { 14493aab70afSSebastian Siewior printf("Bad status: %d\n", req->status); 14503aab70afSSebastian Siewior return; 14513aab70afSSebastian Siewior } 14523aab70afSSebastian Siewior 14533aab70afSSebastian Siewior if (buffer_size < transfer_size) 14543aab70afSSebastian Siewior transfer_size = buffer_size; 14553aab70afSSebastian Siewior 1456a588d99aSPaul Kocialkowski memcpy((void *)CONFIG_FASTBOOT_BUF_ADDR + download_bytes, 14573aab70afSSebastian Siewior buffer, transfer_size); 14583aab70afSSebastian Siewior 145923d1d10cSBo Shen pre_dot_num = download_bytes / BYTES_PER_DOT; 14603aab70afSSebastian Siewior download_bytes += transfer_size; 146123d1d10cSBo Shen now_dot_num = download_bytes / BYTES_PER_DOT; 146223d1d10cSBo Shen 146323d1d10cSBo Shen if (pre_dot_num != now_dot_num) { 146423d1d10cSBo Shen putc('.'); 146523d1d10cSBo Shen if (!(now_dot_num % 74)) 146623d1d10cSBo Shen putc('\n'); 146723d1d10cSBo Shen } 14683aab70afSSebastian Siewior 14693aab70afSSebastian Siewior /* Check if transfer is done */ 14703aab70afSSebastian Siewior if (download_bytes >= download_size) { 14713aab70afSSebastian Siewior /* 14723aab70afSSebastian Siewior * Reset global transfer variable, keep download_bytes because 14733aab70afSSebastian Siewior * it will be used in the next possible flashing command 14743aab70afSSebastian Siewior */ 14753aab70afSSebastian Siewior download_size = 0; 14763aab70afSSebastian Siewior req->complete = rx_handler_command; 14773aab70afSSebastian Siewior req->length = EP_BUFFER_SIZE; 14783aab70afSSebastian Siewior 1479192bc694SBen Whitten strcpy(response, "OKAY"); 14803aab70afSSebastian Siewior fastboot_tx_write_str(response); 14813aab70afSSebastian Siewior 14823aab70afSSebastian Siewior printf("\ndownloading of %d bytes finished\n", download_bytes); 14833aab70afSSebastian Siewior } else { 1484ac484c5aSRoger Quadros req->length = rx_bytes_expected(ep); 14853aab70afSSebastian Siewior } 14863aab70afSSebastian Siewior 14873aab70afSSebastian Siewior req->actual = 0; 14883aab70afSSebastian Siewior usb_ep_queue(ep, req, 0); 14893aab70afSSebastian Siewior } 14903aab70afSSebastian Siewior 14913aab70afSSebastian Siewior static void cb_download(struct usb_ep *ep, struct usb_request *req) 14923aab70afSSebastian Siewior { 14933aab70afSSebastian Siewior char *cmd = req->buf; 14943c8f98f5SMaxime Ripard char response[FASTBOOT_RESPONSE_LEN]; 14953aab70afSSebastian Siewior 14963aab70afSSebastian Siewior strsep(&cmd, ":"); 14973aab70afSSebastian Siewior download_size = simple_strtoul(cmd, NULL, 16); 14983aab70afSSebastian Siewior download_bytes = 0; 14993aab70afSSebastian Siewior 15003aab70afSSebastian Siewior printf("Starting download of %d bytes\n", download_size); 15013aab70afSSebastian Siewior 15023aab70afSSebastian Siewior if (0 == download_size) { 1503192bc694SBen Whitten strcpy(response, "FAILdata invalid size"); 1504a588d99aSPaul Kocialkowski } else if (download_size > CONFIG_FASTBOOT_BUF_SIZE) { 15053aab70afSSebastian Siewior download_size = 0; 1506192bc694SBen Whitten strcpy(response, "FAILdata too large"); 15073aab70afSSebastian Siewior } else { 15083aab70afSSebastian Siewior sprintf(response, "DATA%08x", download_size); 15093aab70afSSebastian Siewior req->complete = rx_handler_dl_image; 1510ac484c5aSRoger Quadros req->length = rx_bytes_expected(ep); 15113aab70afSSebastian Siewior } 1512367cce4dSXu Hongfei 1513367cce4dSXu Hongfei fastboot_tx_write_str(response); 1514367cce4dSXu Hongfei } 1515367cce4dSXu Hongfei 1516367cce4dSXu Hongfei static void tx_handler_ul(struct usb_ep *ep, struct usb_request *req) 1517367cce4dSXu Hongfei { 1518367cce4dSXu Hongfei unsigned int xfer_size = 0; 1519367cce4dSXu Hongfei unsigned int pre_dot_num, now_dot_num; 1520367cce4dSXu Hongfei unsigned int remain_size = 0; 1521367cce4dSXu Hongfei unsigned int transferred_size = req->actual; 1522367cce4dSXu Hongfei 1523367cce4dSXu Hongfei if (req->status != 0) { 1524367cce4dSXu Hongfei printf("Bad status: %d\n", req->status); 1525367cce4dSXu Hongfei return; 1526367cce4dSXu Hongfei } 1527367cce4dSXu Hongfei 1528367cce4dSXu Hongfei if (start_upload) { 1529367cce4dSXu Hongfei pre_dot_num = upload_bytes / BYTES_PER_DOT; 1530367cce4dSXu Hongfei upload_bytes += transferred_size; 1531367cce4dSXu Hongfei now_dot_num = upload_bytes / BYTES_PER_DOT; 1532367cce4dSXu Hongfei 1533367cce4dSXu Hongfei if (pre_dot_num != now_dot_num) { 1534367cce4dSXu Hongfei putc('.'); 1535367cce4dSXu Hongfei if (!(now_dot_num % 74)) 1536367cce4dSXu Hongfei putc('\n'); 1537367cce4dSXu Hongfei } 1538367cce4dSXu Hongfei } 1539367cce4dSXu Hongfei 1540367cce4dSXu Hongfei remain_size = upload_size - upload_bytes; 1541367cce4dSXu Hongfei xfer_size = (remain_size > EP_BUFFER_SIZE) ? 1542367cce4dSXu Hongfei EP_BUFFER_SIZE : remain_size; 1543367cce4dSXu Hongfei 1544367cce4dSXu Hongfei debug("%s: remain_size=%d, transferred_size=%d", 1545367cce4dSXu Hongfei __func__, remain_size, transferred_size); 1546367cce4dSXu Hongfei debug("xfer_size=%d, upload_bytes=%d, upload_size=%d!\n", 1547367cce4dSXu Hongfei xfer_size, upload_bytes, upload_size); 1548367cce4dSXu Hongfei 1549367cce4dSXu Hongfei if (remain_size <= 0) { 1550367cce4dSXu Hongfei fastboot_func->in_req->complete = fastboot_complete; 1551aa74b607SJason Zhu wakeup_thread(); 1552367cce4dSXu Hongfei fastboot_tx_write_str("OKAY"); 1553367cce4dSXu Hongfei printf("\nuploading of %d bytes finished\n", upload_bytes); 1554367cce4dSXu Hongfei upload_bytes = 0; 1555367cce4dSXu Hongfei upload_size = 0; 1556367cce4dSXu Hongfei start_upload = false; 1557367cce4dSXu Hongfei return; 1558367cce4dSXu Hongfei } 1559367cce4dSXu Hongfei 1560367cce4dSXu Hongfei /* Remove the transfer callback which response the upload */ 1561367cce4dSXu Hongfei /* request from host */ 1562367cce4dSXu Hongfei if (!upload_bytes) 1563367cce4dSXu Hongfei start_upload = true; 1564367cce4dSXu Hongfei 1565174b4544SKever Yang fastboot_tx_write((char *)((phys_addr_t)CONFIG_FASTBOOT_BUF_ADDR + \ 1566174b4544SKever Yang upload_bytes), 1567367cce4dSXu Hongfei xfer_size); 1568367cce4dSXu Hongfei } 1569367cce4dSXu Hongfei 1570367cce4dSXu Hongfei static void cb_upload(struct usb_ep *ep, struct usb_request *req) 1571367cce4dSXu Hongfei { 1572367cce4dSXu Hongfei char response[FASTBOOT_RESPONSE_LEN]; 1573367cce4dSXu Hongfei 1574367cce4dSXu Hongfei printf("Starting upload of %d bytes\n", upload_size); 1575367cce4dSXu Hongfei 1576367cce4dSXu Hongfei if (0 == upload_size) { 1577367cce4dSXu Hongfei strcpy(response, "FAILdata invalid size"); 1578367cce4dSXu Hongfei } else { 1579367cce4dSXu Hongfei start_upload = false; 1580367cce4dSXu Hongfei sprintf(response, "DATA%08x", upload_size); 1581367cce4dSXu Hongfei fastboot_func->in_req->complete = tx_handler_ul; 1582367cce4dSXu Hongfei } 1583367cce4dSXu Hongfei 15843aab70afSSebastian Siewior fastboot_tx_write_str(response); 15853aab70afSSebastian Siewior } 15863aab70afSSebastian Siewior 15873aab70afSSebastian Siewior static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req) 15883aab70afSSebastian Siewior { 15893aab70afSSebastian Siewior char boot_addr_start[12]; 15903aab70afSSebastian Siewior char *bootm_args[] = { "bootm", boot_addr_start, NULL }; 15913aab70afSSebastian Siewior 15923aab70afSSebastian Siewior puts("Booting kernel..\n"); 15933aab70afSSebastian Siewior 159490ae53ceSTom Rini sprintf(boot_addr_start, "0x%lx", (long)CONFIG_FASTBOOT_BUF_ADDR); 15953aab70afSSebastian Siewior do_bootm(NULL, 0, 2, bootm_args); 15963aab70afSSebastian Siewior 15973aab70afSSebastian Siewior /* This only happens if image is somehow faulty so we start over */ 15983aab70afSSebastian Siewior do_reset(NULL, 0, 0, NULL); 15993aab70afSSebastian Siewior } 16003aab70afSSebastian Siewior 16013aab70afSSebastian Siewior static void cb_boot(struct usb_ep *ep, struct usb_request *req) 16023aab70afSSebastian Siewior { 16033aab70afSSebastian Siewior fastboot_func->in_req->complete = do_bootm_on_complete; 16043aab70afSSebastian Siewior fastboot_tx_write_str("OKAY"); 16053aab70afSSebastian Siewior } 16063aab70afSSebastian Siewior 1607267abc62SRob Herring static void do_exit_on_complete(struct usb_ep *ep, struct usb_request *req) 1608267abc62SRob Herring { 1609267abc62SRob Herring g_dnl_trigger_detach(); 1610267abc62SRob Herring } 1611267abc62SRob Herring 1612267abc62SRob Herring static void cb_continue(struct usb_ep *ep, struct usb_request *req) 1613267abc62SRob Herring { 1614267abc62SRob Herring fastboot_func->in_req->complete = do_exit_on_complete; 1615267abc62SRob Herring fastboot_tx_write_str("OKAY"); 1616267abc62SRob Herring } 1617267abc62SRob Herring 1618367cce4dSXu Hongfei static void cb_set_active(struct usb_ep *ep, struct usb_request *req) 1619367cce4dSXu Hongfei { 1620367cce4dSXu Hongfei char *cmd = req->buf; 1621367cce4dSXu Hongfei 1622367cce4dSXu Hongfei debug("%s: %s\n", __func__, cmd); 1623367cce4dSXu Hongfei 1624367cce4dSXu Hongfei strsep(&cmd, ":"); 1625367cce4dSXu Hongfei if (!cmd) { 162690aa625cSMasahiro Yamada pr_err("missing slot name"); 16272e40c2c1SJason Zhu fastboot_tx_write_str("FAILmissing slot name"); 1628367cce4dSXu Hongfei return; 1629367cce4dSXu Hongfei } 163037a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER 1631367cce4dSXu Hongfei unsigned int slot_number; 1632367cce4dSXu Hongfei if (strncmp("a", cmd, 1) == 0) { 1633367cce4dSXu Hongfei slot_number = 0; 163437a7bc39SJason Zhu rk_avb_set_slot_active(&slot_number); 1635367cce4dSXu Hongfei } else if (strncmp("b", cmd, 1) == 0) { 1636367cce4dSXu Hongfei slot_number = 1; 163737a7bc39SJason Zhu rk_avb_set_slot_active(&slot_number); 1638367cce4dSXu Hongfei } else { 16392e40c2c1SJason Zhu fastboot_tx_write_str("FAILunkown slot name"); 1640367cce4dSXu Hongfei return; 1641367cce4dSXu Hongfei } 1642367cce4dSXu Hongfei 1643367cce4dSXu Hongfei fastboot_tx_write_str("OKAY"); 1644367cce4dSXu Hongfei return; 1645367cce4dSXu Hongfei #else 1646367cce4dSXu Hongfei fastboot_tx_write_str("FAILnot implemented"); 1647367cce4dSXu Hongfei return; 1648367cce4dSXu Hongfei #endif 1649367cce4dSXu Hongfei } 1650367cce4dSXu Hongfei 1651d1b5ed07SSteve Rae #ifdef CONFIG_FASTBOOT_FLASH 1652d1b5ed07SSteve Rae static void cb_flash(struct usb_ep *ep, struct usb_request *req) 1653d1b5ed07SSteve Rae { 1654d1b5ed07SSteve Rae char *cmd = req->buf; 1655374a9995SCody Xie char response[FASTBOOT_RESPONSE_LEN] = {0}; 165637a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER 16577bc1707dSJason Zhu uint8_t flash_lock_state; 1658d1b5ed07SSteve Rae 165937a7bc39SJason Zhu if (rk_avb_read_flash_lock_state(&flash_lock_state)) { 16602e40c2c1SJason Zhu /* write the device flashing unlock when first read */ 16612e40c2c1SJason Zhu if (rk_avb_write_flash_lock_state(1)) { 16622e40c2c1SJason Zhu fastboot_tx_write_str("FAILflash lock state write failure"); 16637bc1707dSJason Zhu return; 1664ef52a073SJason Zhu } 16652e40c2c1SJason Zhu if (rk_avb_read_flash_lock_state(&flash_lock_state)) { 16662e40c2c1SJason Zhu fastboot_tx_write_str("FAILflash lock state read failure"); 16672e40c2c1SJason Zhu return; 16682e40c2c1SJason Zhu } 16692e40c2c1SJason Zhu } 1670ef52a073SJason Zhu 16717bc1707dSJason Zhu if (flash_lock_state == 0) { 16727bc1707dSJason Zhu fastboot_tx_write_str("FAILThe device is locked, can not flash!"); 16737bc1707dSJason Zhu printf("The device is locked, can not flash!\n"); 16747bc1707dSJason Zhu return; 16757bc1707dSJason Zhu } 16767bc1707dSJason Zhu #endif 1677d1b5ed07SSteve Rae strsep(&cmd, ":"); 1678d1b5ed07SSteve Rae if (!cmd) { 167990aa625cSMasahiro Yamada pr_err("missing partition name"); 1680d1b5ed07SSteve Rae fastboot_tx_write_str("FAILmissing partition name"); 1681d1b5ed07SSteve Rae return; 1682d1b5ed07SSteve Rae } 1683d1b5ed07SSteve Rae 16848b464fa9SJocelyn Bohr fastboot_fail("no flash device defined", response); 1685d1b5ed07SSteve Rae #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV 168664ece848SSteve Rae fb_mmc_flash_write(cmd, (void *)CONFIG_FASTBOOT_BUF_ADDR, 16878b464fa9SJocelyn Bohr download_bytes, response); 1688d1b5ed07SSteve Rae #endif 1689bf8940d3SMaxime Ripard #ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV 16908b464fa9SJocelyn Bohr fb_nand_flash_write(cmd, (void *)CONFIG_FASTBOOT_BUF_ADDR, 16918b464fa9SJocelyn Bohr download_bytes, response); 1692bf8940d3SMaxime Ripard #endif 1693d1b5ed07SSteve Rae fastboot_tx_write_str(response); 1694d1b5ed07SSteve Rae } 1695d1b5ed07SSteve Rae 1696367cce4dSXu Hongfei static void cb_flashing(struct usb_ep *ep, struct usb_request *req) 1697367cce4dSXu Hongfei { 1698367cce4dSXu Hongfei char *cmd = req->buf; 1699367cce4dSXu Hongfei 1700367cce4dSXu Hongfei if (strncmp("lock", cmd + 9, 4) == 0) { 170137a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER 17027bc1707dSJason Zhu uint8_t flash_lock_state; 17037bc1707dSJason Zhu flash_lock_state = 0; 170437a7bc39SJason Zhu if (rk_avb_write_flash_lock_state(flash_lock_state)) 17052e40c2c1SJason Zhu fastboot_tx_write_str("FAILflash lock state" 17062e40c2c1SJason Zhu " write failure"); 17077bc1707dSJason Zhu else 17087bc1707dSJason Zhu fastboot_tx_write_str("OKAY"); 17097bc1707dSJason Zhu #else 1710367cce4dSXu Hongfei fastboot_tx_write_str("FAILnot implemented"); 17117bc1707dSJason Zhu #endif 1712367cce4dSXu Hongfei } else if (strncmp("unlock", cmd + 9, 6) == 0) { 171337a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER 17147bc1707dSJason Zhu uint8_t flash_lock_state; 17157bc1707dSJason Zhu flash_lock_state = 1; 171637a7bc39SJason Zhu if (rk_avb_write_flash_lock_state(flash_lock_state)) 17172e40c2c1SJason Zhu fastboot_tx_write_str("FAILflash lock state" 17182e40c2c1SJason Zhu " write failure"); 17197bc1707dSJason Zhu else 17207bc1707dSJason Zhu fastboot_tx_write_str("OKAY"); 17217bc1707dSJason Zhu #else 1722367cce4dSXu Hongfei fastboot_tx_write_str("FAILnot implemented"); 17237bc1707dSJason Zhu #endif 1724367cce4dSXu Hongfei } else if (strncmp("lock_critical", cmd + 9, 12) == 0) { 1725367cce4dSXu Hongfei fastboot_tx_write_str("FAILnot implemented"); 1726367cce4dSXu Hongfei } else if (strncmp("unlock_critical", cmd + 9, 14) == 0) { 1727367cce4dSXu Hongfei fastboot_tx_write_str("FAILnot implemented"); 1728367cce4dSXu Hongfei } else if (strncmp("get_unlock_ability", cmd + 9, 17) == 0) { 1729367cce4dSXu Hongfei fastboot_tx_write_str("FAILnot implemented"); 1730367cce4dSXu Hongfei } else if (strncmp("get_unlock_bootloader_nonce", cmd + 4, 27) == 0) { 1731367cce4dSXu Hongfei fastboot_tx_write_str("FAILnot implemented"); 1732367cce4dSXu Hongfei } else if (strncmp("unlock_bootloader", cmd + 9, 17) == 0) { 1733367cce4dSXu Hongfei fastboot_tx_write_str("FAILnot implemented"); 1734367cce4dSXu Hongfei } else if (strncmp("lock_bootloader", cmd + 9, 15) == 0) { 1735367cce4dSXu Hongfei fastboot_tx_write_str("FAILnot implemented"); 1736367cce4dSXu Hongfei } else { 1737367cce4dSXu Hongfei fastboot_tx_write_str("FAILunknown flashing command"); 1738367cce4dSXu Hongfei } 1739367cce4dSXu Hongfei } 17406f3eb474SKever Yang #endif 1741367cce4dSXu Hongfei 17422e40c2c1SJason Zhu static void cb_oem_perm_attr(void) 17432e40c2c1SJason Zhu { 17442e40c2c1SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER 17452e40c2c1SJason Zhu sha256_context ctx; 17462e40c2c1SJason Zhu uint8_t digest[SHA256_SUM_LEN] = {0}; 17472e40c2c1SJason Zhu uint8_t digest_temp[SHA256_SUM_LEN] = {0}; 17482e40c2c1SJason Zhu uint8_t perm_attr_temp[PERM_ATTR_TOTAL_SIZE] = {0}; 17492e40c2c1SJason Zhu uint8_t flag = 0; 17502e40c2c1SJason Zhu 17512e40c2c1SJason Zhu if (PERM_ATTR_TOTAL_SIZE != download_bytes) { 17522e40c2c1SJason Zhu printf("Permanent attribute size is not equal!\n"); 17532e40c2c1SJason Zhu fastboot_tx_write_str("FAILincorrect perm attribute size"); 17542e40c2c1SJason Zhu return; 17552e40c2c1SJason Zhu } 17562e40c2c1SJason Zhu 17572e40c2c1SJason Zhu if (rk_avb_read_perm_attr_flag(&flag)) { 17582e40c2c1SJason Zhu printf("rk_avb_read_perm_attr_flag error!\n"); 17592e40c2c1SJason Zhu fastboot_tx_write_str("FAILperm attr read failed"); 17602e40c2c1SJason Zhu return; 17612e40c2c1SJason Zhu } 17622e40c2c1SJason Zhu 17632e40c2c1SJason Zhu if (flag == PERM_ATTR_SUCCESS_FLAG) { 17642e40c2c1SJason Zhu if (rk_avb_read_attribute_hash(digest_temp, 17652e40c2c1SJason Zhu SHA256_SUM_LEN)) { 17662e40c2c1SJason Zhu printf("The efuse IO can not be used!\n"); 17672e40c2c1SJason Zhu fastboot_tx_write_str("FAILefuse IO can not be used"); 17682e40c2c1SJason Zhu return; 17692e40c2c1SJason Zhu } 17702e40c2c1SJason Zhu 17712e40c2c1SJason Zhu if (memcmp(digest, digest_temp, SHA256_SUM_LEN) != 0) { 17722e40c2c1SJason Zhu if (rk_avb_read_permanent_attributes(perm_attr_temp, 17732e40c2c1SJason Zhu PERM_ATTR_TOTAL_SIZE)) { 17742e40c2c1SJason Zhu printf("rk_avb_write_permanent_attributes error!\n"); 17752e40c2c1SJason Zhu fastboot_tx_write_str("FAILread perm attr error"); 17762e40c2c1SJason Zhu return; 17772e40c2c1SJason Zhu } 17782e40c2c1SJason Zhu 17792e40c2c1SJason Zhu sha256_starts(&ctx); 17802e40c2c1SJason Zhu sha256_update(&ctx, 17812e40c2c1SJason Zhu (const uint8_t *)perm_attr_temp, 17822e40c2c1SJason Zhu PERM_ATTR_TOTAL_SIZE); 17832e40c2c1SJason Zhu sha256_finish(&ctx, digest); 17842e40c2c1SJason Zhu if (memcmp(digest, digest_temp, SHA256_SUM_LEN) == 0) { 17852e40c2c1SJason Zhu printf("The hash has been written!\n"); 17862e40c2c1SJason Zhu fastboot_tx_write_str("OKAY"); 17872e40c2c1SJason Zhu return; 17882e40c2c1SJason Zhu } 17892e40c2c1SJason Zhu } 17902e40c2c1SJason Zhu 17912e40c2c1SJason Zhu if (rk_avb_write_perm_attr_flag(0)) { 17922e40c2c1SJason Zhu fastboot_tx_write_str("FAILperm attr flag write failure"); 17932e40c2c1SJason Zhu return; 17942e40c2c1SJason Zhu } 17952e40c2c1SJason Zhu } 17962e40c2c1SJason Zhu 17972e40c2c1SJason Zhu if (rk_avb_write_permanent_attributes((uint8_t *) 17982e40c2c1SJason Zhu CONFIG_FASTBOOT_BUF_ADDR, 17992e40c2c1SJason Zhu download_bytes)) { 18002e40c2c1SJason Zhu if (rk_avb_write_perm_attr_flag(0)) { 18012e40c2c1SJason Zhu fastboot_tx_write_str("FAILperm attr flag write failure"); 18022e40c2c1SJason Zhu return; 18032e40c2c1SJason Zhu } 18042e40c2c1SJason Zhu fastboot_tx_write_str("FAILperm attr write failed"); 18052e40c2c1SJason Zhu return; 18062e40c2c1SJason Zhu } 18072e40c2c1SJason Zhu 18082e40c2c1SJason Zhu memset(digest, 0, SHA256_SUM_LEN); 18092e40c2c1SJason Zhu sha256_starts(&ctx); 18102e40c2c1SJason Zhu sha256_update(&ctx, (const uint8_t *)CONFIG_FASTBOOT_BUF_ADDR, 18112e40c2c1SJason Zhu PERM_ATTR_TOTAL_SIZE); 18122e40c2c1SJason Zhu sha256_finish(&ctx, digest); 18132e40c2c1SJason Zhu 18142e40c2c1SJason Zhu if (rk_avb_write_attribute_hash((uint8_t *)digest, 18152e40c2c1SJason Zhu SHA256_SUM_LEN)) { 18162e40c2c1SJason Zhu if (rk_avb_read_attribute_hash(digest_temp, 18172e40c2c1SJason Zhu SHA256_SUM_LEN)) { 18182e40c2c1SJason Zhu printf("The efuse IO can not be used!\n"); 18192e40c2c1SJason Zhu fastboot_tx_write_str("FAILefuse IO can not be used"); 18202e40c2c1SJason Zhu return; 18212e40c2c1SJason Zhu } 18222e40c2c1SJason Zhu if (memcmp(digest, digest_temp, SHA256_SUM_LEN) != 0) { 18232e40c2c1SJason Zhu if (rk_avb_write_perm_attr_flag(0)) { 18242e40c2c1SJason Zhu fastboot_tx_write_str("FAILperm attr flag write failure"); 18252e40c2c1SJason Zhu return; 18262e40c2c1SJason Zhu } 18272e40c2c1SJason Zhu printf("The hash has been written, but is different!\n"); 18282e40c2c1SJason Zhu fastboot_tx_write_str("FAILhash comparison failure"); 18292e40c2c1SJason Zhu return; 18302e40c2c1SJason Zhu } 18312e40c2c1SJason Zhu } 18322e40c2c1SJason Zhu 18332e40c2c1SJason Zhu if (rk_avb_write_perm_attr_flag(PERM_ATTR_SUCCESS_FLAG)) { 18342e40c2c1SJason Zhu fastboot_tx_write_str("FAILperm attr flag write failure"); 18352e40c2c1SJason Zhu return; 18362e40c2c1SJason Zhu } 18372e40c2c1SJason Zhu 18382e40c2c1SJason Zhu fastboot_tx_write_str("OKAY"); 18392e40c2c1SJason Zhu #else 18402e40c2c1SJason Zhu fastboot_tx_write_str("FAILnot implemented"); 18412e40c2c1SJason Zhu #endif 18422e40c2c1SJason Zhu } 18432e40c2c1SJason Zhu 1844de195620SMichael Scott static void cb_oem(struct usb_ep *ep, struct usb_request *req) 1845de195620SMichael Scott { 1846de195620SMichael Scott char *cmd = req->buf; 1847367cce4dSXu Hongfei 18484adef270SMaxime Ripard #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV 1849372d7decSRob Herring if (strncmp("format", cmd + 4, 6) == 0) { 1850372d7decSRob Herring char cmdbuf[32]; 1851372d7decSRob Herring sprintf(cmdbuf, "gpt write mmc %x $partitions", 1852372d7decSRob Herring CONFIG_FASTBOOT_FLASH_MMC_DEV); 1853372d7decSRob Herring if (run_command(cmdbuf, 0)) 18542e40c2c1SJason Zhu fastboot_tx_write_str("FAILmmc write failure"); 1855372d7decSRob Herring else 1856372d7decSRob Herring fastboot_tx_write_str("OKAY"); 1857372d7decSRob Herring } else 1858372d7decSRob Herring #endif 1859de195620SMichael Scott if (strncmp("unlock", cmd + 4, 8) == 0) { 186065413a00SJian Qiu #ifdef CONFIG_FASTBOOT_OEM_UNLOCK 1861f61a997eSqiujian #ifdef CONFIG_RK_AVB_LIBAVB_USER 1862de195620SMichael Scott fastboot_tx_write_str("FAILnot implemented"); 1863f61a997eSqiujian return; 1864f61a997eSqiujian #else 1865f61a997eSqiujian uint8_t unlock = 0; 1866f61a997eSqiujian TEEC_Result result; 1867f61a997eSqiujian debug("oem unlock\n"); 1868f61a997eSqiujian result = trusty_read_oem_unlock(&unlock); 1869f61a997eSqiujian if (result) { 1870f61a997eSqiujian printf("read oem unlock status with error : 0x%x\n", result); 1871f61a997eSqiujian fastboot_tx_write_str("FAILRead oem unlock status failed"); 1872f61a997eSqiujian return; 1873f61a997eSqiujian } 1874f61a997eSqiujian if (unlock) { 1875f61a997eSqiujian printf("oem unlock ignored, device already unlocked\n"); 1876f61a997eSqiujian fastboot_tx_write_str("FAILalready unlocked"); 1877f61a997eSqiujian return; 1878f61a997eSqiujian } 1879f61a997eSqiujian printf("oem unlock requested:\n"); 1880f61a997eSqiujian printf("\tUnlocking forces a factory reset and could\n"); 1881f61a997eSqiujian printf("\topen your device up to a world of hurt. If you\n"); 1882f61a997eSqiujian printf("\tare sure you know what you're doing, then accept\n"); 1883f61a997eSqiujian printf("\tvia 'fastboot oem unlock_accept'.\n"); 1884f61a997eSqiujian env_set("unlock", "unlock"); 1885f61a997eSqiujian fastboot_tx_write_str("OKAY"); 1886f61a997eSqiujian #endif 1887f61a997eSqiujian #else 1888f61a997eSqiujian fastboot_tx_write_str("FAILnot implemented"); 1889f61a997eSqiujian return; 1890f61a997eSqiujian #endif 1891f61a997eSqiujian } else if (strncmp("unlock_accept", cmd + 4, 13) == 0) { 189265413a00SJian Qiu #ifdef CONFIG_FASTBOOT_OEM_UNLOCK 1893f61a997eSqiujian #ifdef CONFIG_RK_AVB_LIBAVB_USER 1894f61a997eSqiujian fastboot_tx_write_str("FAILnot implemented"); 1895f61a997eSqiujian return; 1896f61a997eSqiujian #else 1897f61a997eSqiujian char *unlock = env_get("unlock"); 1898f61a997eSqiujian TEEC_Result result; 1899f61a997eSqiujian debug("oem unlock_accept\n"); 1900f61a997eSqiujian if (unlock == NULL || strncmp("unlock", unlock, 6) != 0) { 1901f61a997eSqiujian printf("oem unlock_accept ignored, not pending\n"); 1902f61a997eSqiujian fastboot_tx_write_str("FAILoem unlock not requested"); 1903f61a997eSqiujian return; 1904f61a997eSqiujian } 1905f61a997eSqiujian env_set("unlock", ""); 1906f61a997eSqiujian printf("Erasing userdata partition\n"); 1907f61a997eSqiujian struct blk_desc *dev_desc; 1908f61a997eSqiujian disk_partition_t part_info; 1909f61a997eSqiujian dev_desc = rockchip_get_bootdev(); 19106651d4c0SJason Zhu if (!dev_desc) { 19116651d4c0SJason Zhu printf("%s: dev_desc is NULL!\n", __func__); 19126651d4c0SJason Zhu return; 19136651d4c0SJason Zhu } 1914f61a997eSqiujian int ret = part_get_info_by_name(dev_desc, "userdata", 1915f61a997eSqiujian &part_info); 1916f61a997eSqiujian if (ret < 0) { 1917f61a997eSqiujian printf("not found userdata partition"); 1918f61a997eSqiujian printf("Erase failed with error %d\n", ret); 1919f61a997eSqiujian fastboot_tx_write_str("FAILErasing userdata failed"); 1920f61a997eSqiujian return; 1921f61a997eSqiujian } 1922f61a997eSqiujian ret = blk_derase(dev_desc, part_info.start, part_info.size); 1923f61a997eSqiujian if (ret != part_info.size) { 1924f61a997eSqiujian printf("Erase failed with error %d\n", ret); 1925f61a997eSqiujian fastboot_tx_write_str("FAILErasing userdata failed"); 1926f61a997eSqiujian return; 1927f61a997eSqiujian } 1928f61a997eSqiujian printf("Erasing succeeded\n"); 1929f61a997eSqiujian 1930f61a997eSqiujian result = trusty_write_oem_unlock(1); 1931f61a997eSqiujian if (result) { 1932f61a997eSqiujian printf("write oem unlock status with error : 0x%x\n", result); 1933f61a997eSqiujian fastboot_tx_write_str("FAILWrite oem unlock status failed"); 1934f61a997eSqiujian return; 1935f61a997eSqiujian } 1936f61a997eSqiujian fastboot_tx_write_str("OKAY"); 1937f61a997eSqiujian 1938f61a997eSqiujian /* 1939f61a997eSqiujian * now reboot into recovery to do a format of the 1940f61a997eSqiujian * userdata partition so it's ready to use on next boot 1941f61a997eSqiujian */ 1942f61a997eSqiujian board_run_recovery_wipe_data(); 1943f61a997eSqiujian #endif 1944f61a997eSqiujian #else 1945f61a997eSqiujian fastboot_tx_write_str("FAILnot implemented"); 1946f61a997eSqiujian return; 1947f61a997eSqiujian #endif 1948f61a997eSqiujian } else if (strncmp("lock", cmd + 4, 8) == 0) { 194965413a00SJian Qiu #ifdef CONFIG_FASTBOOT_OEM_UNLOCK 1950f61a997eSqiujian #ifdef CONFIG_RK_AVB_LIBAVB_USER 1951f61a997eSqiujian fastboot_tx_write_str("FAILnot implemented"); 1952f61a997eSqiujian return; 1953f61a997eSqiujian #else 1954f61a997eSqiujian TEEC_Result result; 1955f61a997eSqiujian uint8_t unlock = 0; 1956f61a997eSqiujian trusty_read_oem_unlock(&unlock); 1957f61a997eSqiujian if (!unlock) { 1958f61a997eSqiujian printf("oem lock ignored, already locked\n"); 1959f61a997eSqiujian fastboot_tx_write_str("FAILalready locked"); 1960f61a997eSqiujian return; 1961f61a997eSqiujian } 1962f61a997eSqiujian 1963f61a997eSqiujian result = trusty_write_oem_unlock(0); 1964f61a997eSqiujian if (result) { 1965f61a997eSqiujian printf("write oem unlock status with error : 0x%x\n", result); 1966f61a997eSqiujian fastboot_tx_write_str("FAILWrite oem unlock status failed"); 1967f61a997eSqiujian return; 1968f61a997eSqiujian } 1969f61a997eSqiujian fastboot_tx_write_str("OKAY"); 1970f61a997eSqiujian #endif 1971f61a997eSqiujian #else 1972f61a997eSqiujian fastboot_tx_write_str("FAILnot implemented"); 1973f61a997eSqiujian return; 1974f61a997eSqiujian #endif 1975367cce4dSXu Hongfei } else if (strncmp("at-get-ca-request", cmd + 4, 17) == 0) { 19764d0fc665SAndy Ye #ifdef CONFIG_OPTEE_CLIENT 19772e40c2c1SJason Zhu uint8_t out[ATTEST_CA_OUT_SIZE]; 19784d0fc665SAndy Ye uint32_t operation_size = download_bytes; 19792e40c2c1SJason Zhu uint32_t out_len = ATTEST_CA_OUT_SIZE; 19804d0fc665SAndy Ye uint32_t res = 0; 19812e40c2c1SJason Zhu 19822e40c2c1SJason Zhu res = trusty_attest_get_ca((uint8_t *)CONFIG_FASTBOOT_BUF_ADDR, 19832e40c2c1SJason Zhu &operation_size, out, &out_len); 19844d0fc665SAndy Ye if (res) { 19854d0fc665SAndy Ye fastboot_tx_write_str("FAILtrusty_attest_get_ca failed"); 19864d0fc665SAndy Ye return; 19874d0fc665SAndy Ye } 19884d0fc665SAndy Ye upload_size = out_len; 19894d0fc665SAndy Ye memcpy((void *)CONFIG_FASTBOOT_BUF_ADDR, out, out_len); 1990367cce4dSXu Hongfei fastboot_tx_write_str("OKAY"); 19914d0fc665SAndy Ye #else 19924d0fc665SAndy Ye fastboot_tx_write_str("FAILnot implemented"); 19934d0fc665SAndy Ye return; 19944d0fc665SAndy Ye #endif 1995367cce4dSXu Hongfei } else if (strncmp("at-set-ca-response", cmd + 4, 18) == 0) { 19964d0fc665SAndy Ye #ifdef CONFIG_OPTEE_CLIENT 19974d0fc665SAndy Ye uint32_t ca_response_size = download_bytes; 19984d0fc665SAndy Ye uint32_t res = 0; 19992e40c2c1SJason Zhu 20002e40c2c1SJason Zhu res = trusty_attest_set_ca((uint8_t *)CONFIG_FASTBOOT_BUF_ADDR, 20012e40c2c1SJason Zhu &ca_response_size); 20022e40c2c1SJason Zhu if (res) 20034d0fc665SAndy Ye fastboot_tx_write_str("FAILtrusty_attest_set_ca failed"); 20042e40c2c1SJason Zhu else 2005367cce4dSXu Hongfei fastboot_tx_write_str("OKAY"); 20064d0fc665SAndy Ye #else 20074d0fc665SAndy Ye fastboot_tx_write_str("FAILnot implemented"); 20084d0fc665SAndy Ye return; 20094d0fc665SAndy Ye #endif 201062b11485SJason Zhu } else if (strncmp("at-get-vboot-unlock-challenge", cmd + 4, 29) == 0) { 201162b11485SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER 201262b11485SJason Zhu uint32_t challenge_len = 0; 201362b11485SJason Zhu int ret = 0; 201462b11485SJason Zhu 201562b11485SJason Zhu ret = rk_generate_unlock_challenge((void *)CONFIG_FASTBOOT_BUF_ADDR, &challenge_len); 201662b11485SJason Zhu if (ret == 0) { 201762b11485SJason Zhu upload_size = challenge_len; 201862b11485SJason Zhu fastboot_tx_write_str("OKAY"); 201962b11485SJason Zhu } else { 202062b11485SJason Zhu fastboot_tx_write_str("FAILgenerate unlock challenge fail!"); 202162b11485SJason Zhu } 202262b11485SJason Zhu #else 202362b11485SJason Zhu fastboot_tx_write_str("FAILnot implemented"); 202462b11485SJason Zhu return; 202562b11485SJason Zhu #endif 2026367cce4dSXu Hongfei } else if (strncmp("at-lock-vboot", cmd + 4, 13) == 0) { 202737a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER 2028d8bd6e97SJason Zhu uint8_t lock_state; 2029d8bd6e97SJason Zhu lock_state = 0; 203037a7bc39SJason Zhu if (rk_avb_write_lock_state(lock_state)) 20312e40c2c1SJason Zhu fastboot_tx_write_str("FAILwrite lock state failed"); 2032d8bd6e97SJason Zhu else 2033d8bd6e97SJason Zhu fastboot_tx_write_str("OKAY"); 2034d8bd6e97SJason Zhu #else 2035367cce4dSXu Hongfei fastboot_tx_write_str("FAILnot implemented"); 2036d8bd6e97SJason Zhu #endif 2037367cce4dSXu Hongfei } else if (strncmp("at-unlock-vboot", cmd + 4, 15) == 0) { 203837a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER 2039d8bd6e97SJason Zhu uint8_t lock_state; 204046a8a269SJason Zhu bool out_is_trusted = true; 2041542316a9SJason Zhu 204237a7bc39SJason Zhu if (rk_avb_read_lock_state(&lock_state)) 20432e40c2c1SJason Zhu fastboot_tx_write_str("FAILlock sate read failure"); 2044d8bd6e97SJason Zhu if (lock_state >> 1 == 1) { 2045d8bd6e97SJason Zhu fastboot_tx_write_str("FAILThe vboot is disable!"); 2046d8bd6e97SJason Zhu } else { 2047d8bd6e97SJason Zhu lock_state = 1; 204846a8a269SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_ENABLE_ATH_UNLOCK 2049542316a9SJason Zhu if (rk_auth_unlock((void *)CONFIG_FASTBOOT_BUF_ADDR, 2050542316a9SJason Zhu &out_is_trusted)) { 2051542316a9SJason Zhu printf("rk_auth_unlock ops error!\n"); 2052542316a9SJason Zhu fastboot_tx_write_str("FAILrk_auth_unlock ops error!"); 2053542316a9SJason Zhu return; 2054542316a9SJason Zhu } 205546a8a269SJason Zhu #endif 2056542316a9SJason Zhu if (out_is_trusted == true) { 205737a7bc39SJason Zhu if (rk_avb_write_lock_state(lock_state)) 20582e40c2c1SJason Zhu fastboot_tx_write_str("FAILwrite lock state failed"); 2059d8bd6e97SJason Zhu else 2060d8bd6e97SJason Zhu fastboot_tx_write_str("OKAY"); 2061542316a9SJason Zhu } else { 2062542316a9SJason Zhu fastboot_tx_write_str("FAILauthenticated unlock fail"); 2063542316a9SJason Zhu } 2064d8bd6e97SJason Zhu } 2065d8bd6e97SJason Zhu #else 2066367cce4dSXu Hongfei fastboot_tx_write_str("FAILnot implemented"); 2067d8bd6e97SJason Zhu #endif 2068367cce4dSXu Hongfei } else if (strncmp("at-disable-unlock-vboot", cmd + 4, 23) == 0) { 206937a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER 2070d8bd6e97SJason Zhu uint8_t lock_state; 2071d8bd6e97SJason Zhu lock_state = 2; 207237a7bc39SJason Zhu if (rk_avb_write_lock_state(lock_state)) 20732e40c2c1SJason Zhu fastboot_tx_write_str("FAILwrite lock state failed"); 2074d8bd6e97SJason Zhu else 2075d8bd6e97SJason Zhu fastboot_tx_write_str("OKAY"); 2076d8bd6e97SJason Zhu #else 2077367cce4dSXu Hongfei fastboot_tx_write_str("FAILnot implemented"); 2078d8bd6e97SJason Zhu #endif 2079367cce4dSXu Hongfei } else if (strncmp("fuse at-perm-attr", cmd + 4, 16) == 0) { 20802e40c2c1SJason Zhu cb_oem_perm_attr(); 20814e1bbe84SJason Zhu } else if (strncmp("fuse at-bootloader-vboot-key", cmd + 4, 27) == 0) { 208237a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER 20832e40c2c1SJason Zhu sha256_context ctx; 20842e40c2c1SJason Zhu uint8_t digest[SHA256_SUM_LEN]; 20852e40c2c1SJason Zhu 20864e1bbe84SJason Zhu if (download_bytes != VBOOT_KEY_HASH_SIZE) { 20872e40c2c1SJason Zhu fastboot_tx_write_str("FAILinvalid vboot key length"); 20884e1bbe84SJason Zhu printf("The vboot key size error!\n"); 20892e40c2c1SJason Zhu return; 20904e1bbe84SJason Zhu } 20914e1bbe84SJason Zhu 20922e40c2c1SJason Zhu sha256_starts(&ctx); 20932e40c2c1SJason Zhu sha256_update(&ctx, (const uint8_t *)CONFIG_FASTBOOT_BUF_ADDR, 20942e40c2c1SJason Zhu VBOOT_KEY_SIZE); 20952e40c2c1SJason Zhu sha256_finish(&ctx, digest); 20962e40c2c1SJason Zhu 20972e40c2c1SJason Zhu if (rk_avb_write_vbootkey_hash((uint8_t *)digest, 20982e40c2c1SJason Zhu SHA256_SUM_LEN)) { 20992e40c2c1SJason Zhu fastboot_tx_write_str("FAILvbootkey hash write failure"); 21004e1bbe84SJason Zhu return; 21014e1bbe84SJason Zhu } 21024e1bbe84SJason Zhu fastboot_tx_write_str("OKAY"); 21034e1bbe84SJason Zhu #else 21044e1bbe84SJason Zhu fastboot_tx_write_str("FAILnot implemented"); 21054e1bbe84SJason Zhu #endif 2106367cce4dSXu Hongfei } else { 2107de195620SMichael Scott fastboot_tx_write_str("FAILunknown oem command"); 2108de195620SMichael Scott } 2109de195620SMichael Scott } 2110de195620SMichael Scott 211189792381SDileep Katta #ifdef CONFIG_FASTBOOT_FLASH 211289792381SDileep Katta static void cb_erase(struct usb_ep *ep, struct usb_request *req) 211389792381SDileep Katta { 211489792381SDileep Katta char *cmd = req->buf; 21153c8f98f5SMaxime Ripard char response[FASTBOOT_RESPONSE_LEN]; 211689792381SDileep Katta 211789792381SDileep Katta strsep(&cmd, ":"); 211889792381SDileep Katta if (!cmd) { 211990aa625cSMasahiro Yamada pr_err("missing partition name"); 212089792381SDileep Katta fastboot_tx_write_str("FAILmissing partition name"); 212189792381SDileep Katta return; 212289792381SDileep Katta } 212389792381SDileep Katta 21248b464fa9SJocelyn Bohr fastboot_fail("no flash device defined", response); 212589792381SDileep Katta #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV 21268b464fa9SJocelyn Bohr fb_mmc_erase(cmd, response); 212789792381SDileep Katta #endif 2128bf8940d3SMaxime Ripard #ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV 21298b464fa9SJocelyn Bohr fb_nand_erase(cmd, response); 2130bf8940d3SMaxime Ripard #endif 213189792381SDileep Katta fastboot_tx_write_str(response); 213289792381SDileep Katta } 213389792381SDileep Katta #endif 213489792381SDileep Katta 21353aab70afSSebastian Siewior struct cmd_dispatch_info { 21363aab70afSSebastian Siewior char *cmd; 21373aab70afSSebastian Siewior void (*cb)(struct usb_ep *ep, struct usb_request *req); 21383aab70afSSebastian Siewior }; 21393aab70afSSebastian Siewior 21403aab70afSSebastian Siewior static const struct cmd_dispatch_info cmd_dispatch_info[] = { 21413aab70afSSebastian Siewior { 21423aab70afSSebastian Siewior .cmd = "reboot", 21433aab70afSSebastian Siewior .cb = cb_reboot, 21443aab70afSSebastian Siewior }, { 21453aab70afSSebastian Siewior .cmd = "getvar:", 21463aab70afSSebastian Siewior .cb = cb_getvar, 21473aab70afSSebastian Siewior }, { 21483aab70afSSebastian Siewior .cmd = "download:", 21493aab70afSSebastian Siewior .cb = cb_download, 21503aab70afSSebastian Siewior }, { 2151367cce4dSXu Hongfei .cmd = "upload", 2152367cce4dSXu Hongfei .cb = cb_upload, 2153367cce4dSXu Hongfei }, { 21543aab70afSSebastian Siewior .cmd = "boot", 21553aab70afSSebastian Siewior .cb = cb_boot, 2156267abc62SRob Herring }, { 2157267abc62SRob Herring .cmd = "continue", 2158267abc62SRob Herring .cb = cb_continue, 2159367cce4dSXu Hongfei }, { 2160367cce4dSXu Hongfei .cmd = "set_active", 2161367cce4dSXu Hongfei .cb = cb_set_active, 21623aab70afSSebastian Siewior }, 2163d1b5ed07SSteve Rae #ifdef CONFIG_FASTBOOT_FLASH 2164d1b5ed07SSteve Rae { 2165367cce4dSXu Hongfei .cmd = "flashing", 2166367cce4dSXu Hongfei .cb = cb_flashing, 2167367cce4dSXu Hongfei }, 2168367cce4dSXu Hongfei { 2169d1b5ed07SSteve Rae .cmd = "flash", 2170d1b5ed07SSteve Rae .cb = cb_flash, 217189792381SDileep Katta }, { 217289792381SDileep Katta .cmd = "erase", 217389792381SDileep Katta .cb = cb_erase, 2174d1b5ed07SSteve Rae }, 2175d1b5ed07SSteve Rae #endif 2176de195620SMichael Scott { 2177de195620SMichael Scott .cmd = "oem", 2178de195620SMichael Scott .cb = cb_oem, 2179de195620SMichael Scott }, 21803aab70afSSebastian Siewior }; 21813aab70afSSebastian Siewior 21823aab70afSSebastian Siewior static void rx_handler_command(struct usb_ep *ep, struct usb_request *req) 21833aab70afSSebastian Siewior { 21843aab70afSSebastian Siewior char *cmdbuf = req->buf; 21853aab70afSSebastian Siewior void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL; 21863aab70afSSebastian Siewior int i; 21873aab70afSSebastian Siewior 218894b385faSPaul Kocialkowski if (req->status != 0 || req->length == 0) 218994b385faSPaul Kocialkowski return; 219094b385faSPaul Kocialkowski 21913aab70afSSebastian Siewior for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) { 21923aab70afSSebastian Siewior if (!strcmp_l1(cmd_dispatch_info[i].cmd, cmdbuf)) { 21933aab70afSSebastian Siewior func_cb = cmd_dispatch_info[i].cb; 21943aab70afSSebastian Siewior break; 21953aab70afSSebastian Siewior } 21963aab70afSSebastian Siewior } 21973aab70afSSebastian Siewior 2198593cbd93SSteve Rae if (!func_cb) { 219990aa625cSMasahiro Yamada pr_err("unknown command: %.*s", req->actual, cmdbuf); 22003aab70afSSebastian Siewior fastboot_tx_write_str("FAILunknown command"); 2201593cbd93SSteve Rae } else { 2202e2140588SEric Nelson if (req->actual < req->length) { 2203e2140588SEric Nelson u8 *buf = (u8 *)req->buf; 2204e2140588SEric Nelson buf[req->actual] = 0; 22053aab70afSSebastian Siewior func_cb(ep, req); 2206e2140588SEric Nelson } else { 220790aa625cSMasahiro Yamada pr_err("buffer overflow"); 2208e2140588SEric Nelson fastboot_tx_write_str("FAILbuffer overflow"); 2209e2140588SEric Nelson } 2210593cbd93SSteve Rae } 22113aab70afSSebastian Siewior 22123aab70afSSebastian Siewior *cmdbuf = '\0'; 22133aab70afSSebastian Siewior req->actual = 0; 22143aab70afSSebastian Siewior usb_ep_queue(ep, req, 0); 22153aab70afSSebastian Siewior } 2216