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