1 /*
2  * Copyright (c) 2021-2022, STMicroelectronics - All Rights Reserved
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <assert.h>
8 #include <errno.h>
9 #include <string.h>
10 
11 #include <tools_share/firmware_image_package.h>
12 
13 #include <stm32cubeprogrammer.h>
14 #include <usb_dfu.h>
15 
16 /* Undefined download address */
17 #define UNDEFINED_DOWN_ADDR	0xFFFFFFFF
18 
19 struct dfu_state {
20 	uint8_t phase;
21 	uintptr_t base;
22 	size_t len;
23 	uintptr_t address;
24 	/* working buffer */
25 	uint8_t buffer[UCHAR_MAX];
26 };
27 
28 static struct dfu_state dfu_state;
29 
30 /* minimal size of Get Pḧase = offset for additionnl information */
31 #define	GET_PHASE_LEN	9
32 
33 #define DFU_ERROR(...) \
34 	{ \
35 		ERROR(__VA_ARGS__); \
36 		if (dfu->phase != PHASE_RESET) { \
37 			snprintf((char *)&dfu->buffer[GET_PHASE_LEN], \
38 				 sizeof(dfu->buffer) - GET_PHASE_LEN, \
39 				 __VA_ARGS__); \
40 			dfu->phase = PHASE_RESET; \
41 			dfu->address = UNDEFINED_DOWN_ADDR; \
42 			dfu->len = 0; \
43 		} \
44 	}
45 
is_valid_header(fip_toc_header_t * header)46 static bool is_valid_header(fip_toc_header_t *header)
47 {
48 	if ((header->name == TOC_HEADER_NAME) && (header->serial_number != 0U)) {
49 		return true;
50 	}
51 
52 	return false;
53 }
54 
dfu_callback_upload(uint8_t alt,uintptr_t * buffer,uint32_t * len,void * user_data)55 static int dfu_callback_upload(uint8_t alt, uintptr_t *buffer, uint32_t *len,
56 			       void *user_data)
57 {
58 	int result = 0;
59 	uint32_t length = 0;
60 	struct dfu_state *dfu = (struct dfu_state *)user_data;
61 
62 	switch (usb_dfu_get_phase(alt)) {
63 	case PHASE_CMD:
64 		/* Get Pḧase */
65 		dfu->buffer[0] = dfu->phase;
66 		dfu->buffer[1] = (uint8_t)(dfu->address);
67 		dfu->buffer[2] = (uint8_t)(dfu->address >> 8);
68 		dfu->buffer[3] = (uint8_t)(dfu->address >> 16);
69 		dfu->buffer[4] = (uint8_t)(dfu->address >> 24);
70 		dfu->buffer[5] = 0x00;
71 		dfu->buffer[6] = 0x00;
72 		dfu->buffer[7] = 0x00;
73 		dfu->buffer[8] = 0x00;
74 		length = GET_PHASE_LEN;
75 		if (dfu->phase == PHASE_FLASHLAYOUT &&
76 		    dfu->address == UNDEFINED_DOWN_ADDR) {
77 			INFO("Send detach request\n");
78 			dfu->buffer[length++] = 0x01;
79 		}
80 		if (dfu->phase == PHASE_RESET) {
81 			/* error information is added by DFU_ERROR macro */
82 			length += strnlen((char *)&dfu->buffer[GET_PHASE_LEN],
83 					  sizeof(dfu->buffer) - GET_PHASE_LEN)
84 				  - 1;
85 		}
86 		break;
87 
88 	default:
89 		DFU_ERROR("phase ID :%i, alternate %i for phase %i\n",
90 			  dfu->phase, alt, usb_dfu_get_phase(alt));
91 		result = -EIO;
92 		break;
93 	}
94 
95 	if (result == 0) {
96 		*len = length;
97 		*buffer = (uintptr_t)dfu->buffer;
98 	}
99 
100 	return result;
101 }
102 
dfu_callback_download(uint8_t alt,uintptr_t * buffer,uint32_t * len,void * user_data)103 static int dfu_callback_download(uint8_t alt, uintptr_t *buffer, uint32_t *len,
104 				 void *user_data)
105 {
106 	struct dfu_state *dfu = (struct dfu_state *)user_data;
107 
108 	if ((dfu->phase != usb_dfu_get_phase(alt)) ||
109 	    (dfu->address == UNDEFINED_DOWN_ADDR)) {
110 		DFU_ERROR("phase ID :%i, alternate %i, address %x\n",
111 			  dfu->phase, alt, (uint32_t)dfu->address);
112 		return -EIO;
113 	}
114 
115 	VERBOSE("Download %d %lx %x\n", alt, dfu->address, *len);
116 	*buffer = dfu->address;
117 	dfu->address += *len;
118 
119 	if (dfu->address - dfu->base > dfu->len) {
120 		return -EIO;
121 	}
122 
123 	return 0;
124 }
125 
dfu_callback_manifestation(uint8_t alt,void * user_data)126 static int dfu_callback_manifestation(uint8_t alt, void *user_data)
127 {
128 	struct dfu_state *dfu = (struct dfu_state *)user_data;
129 
130 	if (dfu->phase != usb_dfu_get_phase(alt)) {
131 		ERROR("Manifestation phase ID :%i, alternate %i, address %lx\n",
132 		      dfu->phase, alt, dfu->address);
133 		return -EIO;
134 	}
135 
136 	INFO("phase ID :%i, Manifestation %d at %lx\n",
137 	     dfu->phase, alt, dfu->address);
138 
139 	switch (dfu->phase) {
140 	case PHASE_SSBL:
141 		if (!is_valid_header((fip_toc_header_t *)dfu->base)) {
142 			DFU_ERROR("FIP Header check failed for phase %d\n", alt);
143 			return -EIO;
144 		}
145 		VERBOSE("FIP header looks OK.\n");
146 
147 		/* Configure End with request detach */
148 		dfu->phase = PHASE_FLASHLAYOUT;
149 		dfu->address = UNDEFINED_DOWN_ADDR;
150 		dfu->len = 0;
151 		break;
152 	default:
153 		DFU_ERROR("Unknown phase\n");
154 	}
155 
156 	return 0;
157 }
158 
159 /* Open a connection to the USB device */
160 static const struct usb_dfu_media usb_dfu_fops = {
161 	.upload = dfu_callback_upload,
162 	.download = dfu_callback_download,
163 	.manifestation = dfu_callback_manifestation,
164 };
165 
stm32cubeprog_usb_load(struct usb_handle * usb_core_handle,uintptr_t base,size_t len)166 int stm32cubeprog_usb_load(struct usb_handle *usb_core_handle,
167 			   uintptr_t base,
168 			   size_t len)
169 {
170 	int ret;
171 
172 	usb_core_handle->user_data = (void *)&dfu_state;
173 
174 	INFO("DFU USB START...\n");
175 	ret = usb_core_start(usb_core_handle);
176 	if (ret != USBD_OK) {
177 		return -EIO;
178 	}
179 
180 	dfu_state.phase = PHASE_SSBL;
181 	dfu_state.address = base;
182 	dfu_state.base = base;
183 	dfu_state.len = len;
184 
185 	ret = usb_dfu_loop(usb_core_handle, &usb_dfu_fops);
186 	if (ret != USBD_OK) {
187 		return -EIO;
188 	}
189 
190 	INFO("DFU USB STOP...\n");
191 	ret = usb_core_stop(usb_core_handle);
192 	if (ret != USBD_OK) {
193 		return -EIO;
194 	}
195 
196 	return 0;
197 }
198