xref: /aosp_15_r20/external/arm-trusted-firmware/drivers/st/io/io_stm32image.c (revision 54fd6939e177f8ff529b10183254802c76df6d08)
1*54fd6939SJiyong Park /*
2*54fd6939SJiyong Park  * Copyright (c) 2018-2021, ARM Limited and Contributors. All rights reserved.
3*54fd6939SJiyong Park  *
4*54fd6939SJiyong Park  * SPDX-License-Identifier: BSD-3-Clause
5*54fd6939SJiyong Park  */
6*54fd6939SJiyong Park 
7*54fd6939SJiyong Park #include <assert.h>
8*54fd6939SJiyong Park #include <errno.h>
9*54fd6939SJiyong Park #include <stdint.h>
10*54fd6939SJiyong Park #include <string.h>
11*54fd6939SJiyong Park 
12*54fd6939SJiyong Park #include <platform_def.h>
13*54fd6939SJiyong Park 
14*54fd6939SJiyong Park #include <common/debug.h>
15*54fd6939SJiyong Park #include <drivers/io/io_driver.h>
16*54fd6939SJiyong Park #include <drivers/io/io_storage.h>
17*54fd6939SJiyong Park #include <drivers/st/io_stm32image.h>
18*54fd6939SJiyong Park #include <lib/utils.h>
19*54fd6939SJiyong Park #include <plat/common/platform.h>
20*54fd6939SJiyong Park 
21*54fd6939SJiyong Park static uintptr_t backend_dev_handle;
22*54fd6939SJiyong Park static uintptr_t backend_image_spec;
23*54fd6939SJiyong Park static uint32_t *stm32_img;
24*54fd6939SJiyong Park static uint8_t first_lba_buffer[MAX_LBA_SIZE] __aligned(4);
25*54fd6939SJiyong Park static struct stm32image_part_info *current_part;
26*54fd6939SJiyong Park 
27*54fd6939SJiyong Park /* STM32 Image driver functions */
28*54fd6939SJiyong Park static int stm32image_dev_open(const uintptr_t init_params,
29*54fd6939SJiyong Park 			       io_dev_info_t **dev_info);
30*54fd6939SJiyong Park static int stm32image_partition_open(io_dev_info_t *dev_info,
31*54fd6939SJiyong Park 				     const uintptr_t spec, io_entity_t *entity);
32*54fd6939SJiyong Park static int stm32image_partition_size(io_entity_t *entity, size_t *length);
33*54fd6939SJiyong Park static int stm32image_partition_read(io_entity_t *entity, uintptr_t buffer,
34*54fd6939SJiyong Park 				     size_t length, size_t *length_read);
35*54fd6939SJiyong Park static int stm32image_partition_close(io_entity_t *entity);
36*54fd6939SJiyong Park static int stm32image_dev_init(io_dev_info_t *dev_info,
37*54fd6939SJiyong Park 			       const uintptr_t init_params);
38*54fd6939SJiyong Park static int stm32image_dev_close(io_dev_info_t *dev_info);
39*54fd6939SJiyong Park 
40*54fd6939SJiyong Park /* Identify the device type as a virtual driver */
device_type_stm32image(void)41*54fd6939SJiyong Park static io_type_t device_type_stm32image(void)
42*54fd6939SJiyong Park {
43*54fd6939SJiyong Park 	return IO_TYPE_STM32IMAGE;
44*54fd6939SJiyong Park }
45*54fd6939SJiyong Park 
46*54fd6939SJiyong Park static const io_dev_connector_t stm32image_dev_connector = {
47*54fd6939SJiyong Park 	.dev_open = stm32image_dev_open
48*54fd6939SJiyong Park };
49*54fd6939SJiyong Park 
50*54fd6939SJiyong Park static const io_dev_funcs_t stm32image_dev_funcs = {
51*54fd6939SJiyong Park 	.type = device_type_stm32image,
52*54fd6939SJiyong Park 	.open = stm32image_partition_open,
53*54fd6939SJiyong Park 	.size = stm32image_partition_size,
54*54fd6939SJiyong Park 	.read = stm32image_partition_read,
55*54fd6939SJiyong Park 	.close = stm32image_partition_close,
56*54fd6939SJiyong Park 	.dev_init = stm32image_dev_init,
57*54fd6939SJiyong Park 	.dev_close = stm32image_dev_close,
58*54fd6939SJiyong Park };
59*54fd6939SJiyong Park 
60*54fd6939SJiyong Park static io_dev_info_t stm32image_dev_info = {
61*54fd6939SJiyong Park 	.funcs = &stm32image_dev_funcs,
62*54fd6939SJiyong Park 	.info = (uintptr_t)0,
63*54fd6939SJiyong Park };
64*54fd6939SJiyong Park 
65*54fd6939SJiyong Park static struct stm32image_device_info stm32image_dev;
66*54fd6939SJiyong Park 
get_part_idx_by_binary_type(uint32_t binary_type)67*54fd6939SJiyong Park static int get_part_idx_by_binary_type(uint32_t binary_type)
68*54fd6939SJiyong Park {
69*54fd6939SJiyong Park 	int i;
70*54fd6939SJiyong Park 
71*54fd6939SJiyong Park 	for (i = 0; i < STM32_PART_NUM; i++) {
72*54fd6939SJiyong Park 		if (stm32image_dev.part_info[i].binary_type == binary_type) {
73*54fd6939SJiyong Park 			return i;
74*54fd6939SJiyong Park 		}
75*54fd6939SJiyong Park 	}
76*54fd6939SJiyong Park 
77*54fd6939SJiyong Park 	return -EINVAL;
78*54fd6939SJiyong Park }
79*54fd6939SJiyong Park 
80*54fd6939SJiyong Park /* Open a connection to the STM32IMAGE device */
stm32image_dev_open(const uintptr_t init_params,io_dev_info_t ** dev_info)81*54fd6939SJiyong Park static int stm32image_dev_open(const uintptr_t init_params,
82*54fd6939SJiyong Park 			       io_dev_info_t **dev_info)
83*54fd6939SJiyong Park {
84*54fd6939SJiyong Park 	int i;
85*54fd6939SJiyong Park 	struct stm32image_device_info *device_info =
86*54fd6939SJiyong Park 		(struct stm32image_device_info *)init_params;
87*54fd6939SJiyong Park 
88*54fd6939SJiyong Park 	assert(dev_info != NULL);
89*54fd6939SJiyong Park 	*dev_info = (io_dev_info_t *)&stm32image_dev_info;
90*54fd6939SJiyong Park 
91*54fd6939SJiyong Park 	stm32image_dev.device_size = device_info->device_size;
92*54fd6939SJiyong Park 	stm32image_dev.lba_size = device_info->lba_size;
93*54fd6939SJiyong Park 
94*54fd6939SJiyong Park 	for (i = 0; i < STM32_PART_NUM; i++) {
95*54fd6939SJiyong Park 		memcpy(stm32image_dev.part_info[i].name,
96*54fd6939SJiyong Park 		       device_info->part_info[i].name, MAX_PART_NAME_SIZE);
97*54fd6939SJiyong Park 		stm32image_dev.part_info[i].binary_type =
98*54fd6939SJiyong Park 			device_info->part_info[i].binary_type;
99*54fd6939SJiyong Park 		stm32image_dev.part_info[i].part_offset =
100*54fd6939SJiyong Park 			device_info->part_info[i].part_offset;
101*54fd6939SJiyong Park 		stm32image_dev.part_info[i].bkp_offset =
102*54fd6939SJiyong Park 			device_info->part_info[i].bkp_offset;
103*54fd6939SJiyong Park 	}
104*54fd6939SJiyong Park 
105*54fd6939SJiyong Park 	return 0;
106*54fd6939SJiyong Park }
107*54fd6939SJiyong Park 
108*54fd6939SJiyong Park /* Do some basic package checks */
stm32image_dev_init(io_dev_info_t * dev_info,const uintptr_t init_params)109*54fd6939SJiyong Park static int stm32image_dev_init(io_dev_info_t *dev_info,
110*54fd6939SJiyong Park 			       const uintptr_t init_params)
111*54fd6939SJiyong Park {
112*54fd6939SJiyong Park 	int result;
113*54fd6939SJiyong Park 
114*54fd6939SJiyong Park 	if ((backend_dev_handle != 0U) || (backend_image_spec != 0U)) {
115*54fd6939SJiyong Park 		ERROR("STM32 Image io supports only one session\n");
116*54fd6939SJiyong Park 		return -ENOMEM;
117*54fd6939SJiyong Park 	}
118*54fd6939SJiyong Park 
119*54fd6939SJiyong Park 	/* Obtain a reference to the image by querying the platform layer */
120*54fd6939SJiyong Park 	result = plat_get_image_source(STM32_IMAGE_ID, &backend_dev_handle,
121*54fd6939SJiyong Park 				       &backend_image_spec);
122*54fd6939SJiyong Park 	if (result != 0) {
123*54fd6939SJiyong Park 		ERROR("STM32 image error (%i)\n", result);
124*54fd6939SJiyong Park 		return -EINVAL;
125*54fd6939SJiyong Park 	}
126*54fd6939SJiyong Park 
127*54fd6939SJiyong Park 	return result;
128*54fd6939SJiyong Park }
129*54fd6939SJiyong Park 
130*54fd6939SJiyong Park /* Close a connection to the STM32 Image device */
stm32image_dev_close(io_dev_info_t * dev_info)131*54fd6939SJiyong Park static int stm32image_dev_close(io_dev_info_t *dev_info)
132*54fd6939SJiyong Park {
133*54fd6939SJiyong Park 	backend_dev_handle = 0U;
134*54fd6939SJiyong Park 	backend_image_spec = 0U;
135*54fd6939SJiyong Park 	stm32_img = NULL;
136*54fd6939SJiyong Park 
137*54fd6939SJiyong Park 	return 0;
138*54fd6939SJiyong Park }
139*54fd6939SJiyong Park 
140*54fd6939SJiyong Park /* Open a partition */
stm32image_partition_open(io_dev_info_t * dev_info,const uintptr_t spec,io_entity_t * entity)141*54fd6939SJiyong Park static int stm32image_partition_open(io_dev_info_t *dev_info,
142*54fd6939SJiyong Park 				     const uintptr_t spec, io_entity_t *entity)
143*54fd6939SJiyong Park {
144*54fd6939SJiyong Park 	const struct stm32image_part_info *partition_spec;
145*54fd6939SJiyong Park 	int idx;
146*54fd6939SJiyong Park 
147*54fd6939SJiyong Park 	assert(entity != NULL);
148*54fd6939SJiyong Park 
149*54fd6939SJiyong Park 	partition_spec = (struct stm32image_part_info *)spec;
150*54fd6939SJiyong Park 	assert(partition_spec != NULL);
151*54fd6939SJiyong Park 
152*54fd6939SJiyong Park 	idx = get_part_idx_by_binary_type(partition_spec->binary_type);
153*54fd6939SJiyong Park 	if ((idx < 0) || (idx > STM32_PART_NUM)) {
154*54fd6939SJiyong Park 		ERROR("Wrong partition index (%d)\n", idx);
155*54fd6939SJiyong Park 		return -EINVAL;
156*54fd6939SJiyong Park 	}
157*54fd6939SJiyong Park 
158*54fd6939SJiyong Park 	current_part = &stm32image_dev.part_info[idx];
159*54fd6939SJiyong Park 	stm32_img = (uint32_t *)&current_part->part_offset;
160*54fd6939SJiyong Park 
161*54fd6939SJiyong Park 	return 0;
162*54fd6939SJiyong Park }
163*54fd6939SJiyong Park 
164*54fd6939SJiyong Park /* Return the size of a partition */
stm32image_partition_size(io_entity_t * entity,size_t * length)165*54fd6939SJiyong Park static int stm32image_partition_size(io_entity_t *entity, size_t *length)
166*54fd6939SJiyong Park {
167*54fd6939SJiyong Park 	int result;
168*54fd6939SJiyong Park 	uintptr_t backend_handle;
169*54fd6939SJiyong Park 	size_t bytes_read;
170*54fd6939SJiyong Park 	boot_api_image_header_t *header =
171*54fd6939SJiyong Park 		(boot_api_image_header_t *)first_lba_buffer;
172*54fd6939SJiyong Park 
173*54fd6939SJiyong Park 	assert(entity != NULL);
174*54fd6939SJiyong Park 	assert(length != NULL);
175*54fd6939SJiyong Park 
176*54fd6939SJiyong Park 	/* Attempt to access the image */
177*54fd6939SJiyong Park 	result = io_open(backend_dev_handle, backend_image_spec,
178*54fd6939SJiyong Park 			 &backend_handle);
179*54fd6939SJiyong Park 
180*54fd6939SJiyong Park 	if (result < 0) {
181*54fd6939SJiyong Park 		ERROR("%s: io_open (%i)\n", __func__, result);
182*54fd6939SJiyong Park 		return result;
183*54fd6939SJiyong Park 	}
184*54fd6939SJiyong Park 
185*54fd6939SJiyong Park 	/* Reset magic header value */
186*54fd6939SJiyong Park 	header->magic = 0;
187*54fd6939SJiyong Park 
188*54fd6939SJiyong Park 	while (header->magic == 0U) {
189*54fd6939SJiyong Park 		result = io_seek(backend_handle, IO_SEEK_SET, *stm32_img);
190*54fd6939SJiyong Park 		if (result != 0) {
191*54fd6939SJiyong Park 			ERROR("%s: io_seek (%i)\n", __func__, result);
192*54fd6939SJiyong Park 			break;
193*54fd6939SJiyong Park 		}
194*54fd6939SJiyong Park 
195*54fd6939SJiyong Park 		result = io_read(backend_handle, (uintptr_t)header,
196*54fd6939SJiyong Park 				 MAX_LBA_SIZE, (size_t *)&bytes_read);
197*54fd6939SJiyong Park 		if (result != 0) {
198*54fd6939SJiyong Park 			if (current_part->bkp_offset == 0U) {
199*54fd6939SJiyong Park 				ERROR("%s: io_read (%i)\n", __func__, result);
200*54fd6939SJiyong Park 			}
201*54fd6939SJiyong Park 			header->magic = 0;
202*54fd6939SJiyong Park 		}
203*54fd6939SJiyong Park 
204*54fd6939SJiyong Park 		if ((header->magic != BOOT_API_IMAGE_HEADER_MAGIC_NB) ||
205*54fd6939SJiyong Park 		    (header->binary_type != current_part->binary_type) ||
206*54fd6939SJiyong Park 		    (header->image_length >= stm32image_dev.device_size)) {
207*54fd6939SJiyong Park 			VERBOSE("%s: partition %s not found at %x\n",
208*54fd6939SJiyong Park 				__func__, current_part->name, *stm32_img);
209*54fd6939SJiyong Park 
210*54fd6939SJiyong Park 			if (current_part->bkp_offset == 0U) {
211*54fd6939SJiyong Park 				result = -ENOMEM;
212*54fd6939SJiyong Park 				break;
213*54fd6939SJiyong Park 			}
214*54fd6939SJiyong Park 
215*54fd6939SJiyong Park 			/* Header not correct, check next offset for backup */
216*54fd6939SJiyong Park 			*stm32_img += current_part->bkp_offset;
217*54fd6939SJiyong Park 			if (*stm32_img > stm32image_dev.device_size) {
218*54fd6939SJiyong Park 				/* No backup found, end of device reached */
219*54fd6939SJiyong Park 				WARN("%s : partition %s not found\n",
220*54fd6939SJiyong Park 				     __func__, current_part->name);
221*54fd6939SJiyong Park 				result = -ENOMEM;
222*54fd6939SJiyong Park 				break;
223*54fd6939SJiyong Park 			}
224*54fd6939SJiyong Park 			header->magic = 0;
225*54fd6939SJiyong Park 		}
226*54fd6939SJiyong Park 	}
227*54fd6939SJiyong Park 
228*54fd6939SJiyong Park 	io_close(backend_handle);
229*54fd6939SJiyong Park 
230*54fd6939SJiyong Park 	if (result != 0) {
231*54fd6939SJiyong Park 		return result;
232*54fd6939SJiyong Park 	}
233*54fd6939SJiyong Park 
234*54fd6939SJiyong Park 	if (header->image_length < stm32image_dev.lba_size) {
235*54fd6939SJiyong Park 		*length = stm32image_dev.lba_size;
236*54fd6939SJiyong Park 	} else {
237*54fd6939SJiyong Park 		*length = header->image_length;
238*54fd6939SJiyong Park 	}
239*54fd6939SJiyong Park 
240*54fd6939SJiyong Park 	INFO("STM32 Image size : %lu\n", (unsigned long)*length);
241*54fd6939SJiyong Park 
242*54fd6939SJiyong Park 	return 0;
243*54fd6939SJiyong Park }
244*54fd6939SJiyong Park 
245*54fd6939SJiyong Park /* Read data from a partition */
stm32image_partition_read(io_entity_t * entity,uintptr_t buffer,size_t length,size_t * length_read)246*54fd6939SJiyong Park static int stm32image_partition_read(io_entity_t *entity, uintptr_t buffer,
247*54fd6939SJiyong Park 				     size_t length, size_t *length_read)
248*54fd6939SJiyong Park {
249*54fd6939SJiyong Park 	int result = -EINVAL;
250*54fd6939SJiyong Park 	uint8_t *local_buffer;
251*54fd6939SJiyong Park 	boot_api_image_header_t *header =
252*54fd6939SJiyong Park 		(boot_api_image_header_t *)first_lba_buffer;
253*54fd6939SJiyong Park 	size_t hdr_sz = sizeof(boot_api_image_header_t);
254*54fd6939SJiyong Park 
255*54fd6939SJiyong Park 	assert(entity != NULL);
256*54fd6939SJiyong Park 	assert(buffer != 0U);
257*54fd6939SJiyong Park 	assert(length_read != NULL);
258*54fd6939SJiyong Park 
259*54fd6939SJiyong Park 	local_buffer = (uint8_t *)buffer;
260*54fd6939SJiyong Park 	*length_read = 0U;
261*54fd6939SJiyong Park 
262*54fd6939SJiyong Park 	while (*length_read == 0U) {
263*54fd6939SJiyong Park 		int offset;
264*54fd6939SJiyong Park 		int local_length;
265*54fd6939SJiyong Park 		uintptr_t backend_handle;
266*54fd6939SJiyong Park 
267*54fd6939SJiyong Park 		if (header->magic != BOOT_API_IMAGE_HEADER_MAGIC_NB) {
268*54fd6939SJiyong Park 			/* Check for backup as image is corrupted */
269*54fd6939SJiyong Park 			if (current_part->bkp_offset == 0U) {
270*54fd6939SJiyong Park 				result = -ENOMEM;
271*54fd6939SJiyong Park 				break;
272*54fd6939SJiyong Park 			}
273*54fd6939SJiyong Park 
274*54fd6939SJiyong Park 			*stm32_img += current_part->bkp_offset;
275*54fd6939SJiyong Park 			if (*stm32_img >= stm32image_dev.device_size) {
276*54fd6939SJiyong Park 				/* End of device reached */
277*54fd6939SJiyong Park 				result = -ENOMEM;
278*54fd6939SJiyong Park 				break;
279*54fd6939SJiyong Park 			}
280*54fd6939SJiyong Park 
281*54fd6939SJiyong Park 			local_buffer = (uint8_t *)buffer;
282*54fd6939SJiyong Park 
283*54fd6939SJiyong Park 			result = stm32image_partition_size(entity, &length);
284*54fd6939SJiyong Park 			if (result != 0) {
285*54fd6939SJiyong Park 				break;
286*54fd6939SJiyong Park 			}
287*54fd6939SJiyong Park 		}
288*54fd6939SJiyong Park 
289*54fd6939SJiyong Park 		/* Part of image already loaded with the header */
290*54fd6939SJiyong Park 		memcpy(local_buffer, (uint8_t *)first_lba_buffer + hdr_sz,
291*54fd6939SJiyong Park 		       MAX_LBA_SIZE - hdr_sz);
292*54fd6939SJiyong Park 		local_buffer += MAX_LBA_SIZE - hdr_sz;
293*54fd6939SJiyong Park 		offset = MAX_LBA_SIZE;
294*54fd6939SJiyong Park 
295*54fd6939SJiyong Park 		/* New image length to be read */
296*54fd6939SJiyong Park 		local_length = round_up(length - ((MAX_LBA_SIZE) - hdr_sz),
297*54fd6939SJiyong Park 					stm32image_dev.lba_size);
298*54fd6939SJiyong Park 
299*54fd6939SJiyong Park 		if ((header->load_address != 0U) &&
300*54fd6939SJiyong Park 		    (header->load_address != buffer)) {
301*54fd6939SJiyong Park 			ERROR("Wrong load address\n");
302*54fd6939SJiyong Park 			panic();
303*54fd6939SJiyong Park 		}
304*54fd6939SJiyong Park 
305*54fd6939SJiyong Park 		result = io_open(backend_dev_handle, backend_image_spec,
306*54fd6939SJiyong Park 				 &backend_handle);
307*54fd6939SJiyong Park 
308*54fd6939SJiyong Park 		if (result != 0) {
309*54fd6939SJiyong Park 			ERROR("%s: io_open (%i)\n", __func__, result);
310*54fd6939SJiyong Park 			break;
311*54fd6939SJiyong Park 		}
312*54fd6939SJiyong Park 
313*54fd6939SJiyong Park 		result = io_seek(backend_handle, IO_SEEK_SET,
314*54fd6939SJiyong Park 				 *stm32_img + offset);
315*54fd6939SJiyong Park 
316*54fd6939SJiyong Park 		if (result != 0) {
317*54fd6939SJiyong Park 			ERROR("%s: io_seek (%i)\n", __func__, result);
318*54fd6939SJiyong Park 			*length_read = 0;
319*54fd6939SJiyong Park 			io_close(backend_handle);
320*54fd6939SJiyong Park 			break;
321*54fd6939SJiyong Park 		}
322*54fd6939SJiyong Park 
323*54fd6939SJiyong Park 		result = io_read(backend_handle, (uintptr_t)local_buffer,
324*54fd6939SJiyong Park 				 local_length, length_read);
325*54fd6939SJiyong Park 
326*54fd6939SJiyong Park 		/* Adding part of size already read from header */
327*54fd6939SJiyong Park 		*length_read += MAX_LBA_SIZE - hdr_sz;
328*54fd6939SJiyong Park 
329*54fd6939SJiyong Park 		if (result != 0) {
330*54fd6939SJiyong Park 			ERROR("%s: io_read (%i)\n", __func__, result);
331*54fd6939SJiyong Park 			*length_read = 0;
332*54fd6939SJiyong Park 			header->magic = 0;
333*54fd6939SJiyong Park 			continue;
334*54fd6939SJiyong Park 		}
335*54fd6939SJiyong Park 
336*54fd6939SJiyong Park 		result = stm32mp_check_header(header, buffer);
337*54fd6939SJiyong Park 		if (result != 0) {
338*54fd6939SJiyong Park 			ERROR("Header check failed\n");
339*54fd6939SJiyong Park 			*length_read = 0;
340*54fd6939SJiyong Park 			header->magic = 0;
341*54fd6939SJiyong Park 		}
342*54fd6939SJiyong Park 
343*54fd6939SJiyong Park 		result = stm32mp_auth_image(header, buffer);
344*54fd6939SJiyong Park 		if (result != 0) {
345*54fd6939SJiyong Park 			ERROR("Authentication Failed (%i)\n", result);
346*54fd6939SJiyong Park 			return result;
347*54fd6939SJiyong Park 		}
348*54fd6939SJiyong Park 
349*54fd6939SJiyong Park 		inv_dcache_range(round_up((uintptr_t)(local_buffer + length - hdr_sz),
350*54fd6939SJiyong Park 					  CACHE_WRITEBACK_GRANULE), *length_read - length + hdr_sz);
351*54fd6939SJiyong Park 
352*54fd6939SJiyong Park 		io_close(backend_handle);
353*54fd6939SJiyong Park 	}
354*54fd6939SJiyong Park 
355*54fd6939SJiyong Park 	return result;
356*54fd6939SJiyong Park }
357*54fd6939SJiyong Park 
358*54fd6939SJiyong Park /* Close a partition */
stm32image_partition_close(io_entity_t * entity)359*54fd6939SJiyong Park static int stm32image_partition_close(io_entity_t *entity)
360*54fd6939SJiyong Park {
361*54fd6939SJiyong Park 	current_part = NULL;
362*54fd6939SJiyong Park 
363*54fd6939SJiyong Park 	return 0;
364*54fd6939SJiyong Park }
365*54fd6939SJiyong Park 
366*54fd6939SJiyong Park /* Register the stm32image driver with the IO abstraction */
register_io_dev_stm32image(const io_dev_connector_t ** dev_con)367*54fd6939SJiyong Park int register_io_dev_stm32image(const io_dev_connector_t **dev_con)
368*54fd6939SJiyong Park {
369*54fd6939SJiyong Park 	int result;
370*54fd6939SJiyong Park 
371*54fd6939SJiyong Park 	assert(dev_con != NULL);
372*54fd6939SJiyong Park 
373*54fd6939SJiyong Park 	result = io_register_device(&stm32image_dev_info);
374*54fd6939SJiyong Park 	if (result == 0) {
375*54fd6939SJiyong Park 		*dev_con = &stm32image_dev_connector;
376*54fd6939SJiyong Park 	}
377*54fd6939SJiyong Park 
378*54fd6939SJiyong Park 	return result;
379*54fd6939SJiyong Park }
380