• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2000-2004
4  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5  *
6  * (C) Copyright 2011
7  * Texas Instruments, <www.ti.com>
8  *
9  * Matt Porter <mporter@ti.com>
10  */
11 #include <common.h>
12 #include <gzip.h>
13 #include <spl.h>
14 #include <xyzModem.h>
15 #include <asm/u-boot.h>
16 #include <linux/libfdt.h>
17 
18 #define BUF_SIZE 1024
19 
20 /*
21  * Information required to load image using ymodem.
22  *
23  * @image_read: Now of bytes read from the image.
24  * @buf: pointer to the previous read block.
25  */
26 struct ymodem_fit_info {
27 	int image_read;
28 	char *buf;
29 };
30 
getcymodem(void)31 static int getcymodem(void) {
32 	if (tstc())
33 		return (getc());
34 	return -1;
35 }
36 
ymodem_read_fit(struct spl_load_info * load,ulong offset,ulong size,void * addr)37 static ulong ymodem_read_fit(struct spl_load_info *load, ulong offset,
38 			     ulong size, void *addr)
39 {
40 	int res, err, buf_offset;
41 	struct ymodem_fit_info *info = load->priv;
42 	char *buf = info->buf;
43 
44 	while (info->image_read < offset) {
45 		res = xyzModem_stream_read(buf, BUF_SIZE, &err);
46 		if (res <= 0)
47 			break;
48 
49 		info->image_read += res;
50 	}
51 
52 	if (info->image_read > offset) {
53 		res = info->image_read - offset;
54 		if (info->image_read % BUF_SIZE)
55 			buf_offset = (info->image_read % BUF_SIZE);
56 		else
57 			buf_offset = BUF_SIZE;
58 		memcpy(addr, &buf[buf_offset - res], res);
59 		addr = addr + res;
60 	}
61 
62 	while (info->image_read < offset + size) {
63 		res = xyzModem_stream_read(buf, BUF_SIZE, &err);
64 		if (res <= 0)
65 			break;
66 
67 		memcpy(addr, buf, res);
68 		info->image_read += res;
69 		addr += res;
70 	}
71 
72 	return size;
73 }
74 
spl_ymodem_load_image(struct spl_image_info * spl_image,struct spl_boot_device * bootdev)75 int spl_ymodem_load_image(struct spl_image_info *spl_image,
76 			  struct spl_boot_device *bootdev)
77 {
78 	ulong size = 0;
79 	int err;
80 	int res;
81 	int ret;
82 	connection_info_t info;
83 	char buf[BUF_SIZE];
84 	struct image_header *ih = NULL;
85 	ulong addr = 0;
86 
87 	info.mode = xyzModem_ymodem;
88 	ret = xyzModem_stream_open(&info, &err);
89 	if (ret) {
90 		printf("spl: ymodem err - %s\n", xyzModem_error(err));
91 		return ret;
92 	}
93 
94 	res = xyzModem_stream_read(buf, BUF_SIZE, &err);
95 	if (res <= 0)
96 		goto end_stream;
97 
98 	if (IS_ENABLED(CONFIG_SPL_LOAD_FIT_FULL) &&
99 	    image_get_magic((struct image_header *)buf) == FDT_MAGIC) {
100 		addr = CONFIG_SYS_LOAD_ADDR;
101 		ih = (struct image_header *)addr;
102 
103 		memcpy((void *)addr, buf, res);
104 		size += res;
105 		addr += res;
106 
107 		while ((res = xyzModem_stream_read(buf, BUF_SIZE, &err)) > 0) {
108 			memcpy((void *)addr, buf, res);
109 			size += res;
110 			addr += res;
111 		}
112 
113 		ret = spl_parse_image_header(spl_image, ih);
114 		if (ret)
115 			return ret;
116 	} else if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) &&
117 	    image_get_magic((struct image_header *)buf) == FDT_MAGIC) {
118 		struct spl_load_info load;
119 		struct ymodem_fit_info info;
120 
121 		debug("Found FIT\n");
122 		load.dev = NULL;
123 		load.priv = (void *)&info;
124 		load.filename = NULL;
125 		load.bl_len = 1;
126 		info.buf = buf;
127 		info.image_read = BUF_SIZE;
128 		load.read = ymodem_read_fit;
129 		ret = spl_load_simple_fit(spl_image, &load, 0, (void *)buf);
130 		size = info.image_read;
131 
132 		while ((res = xyzModem_stream_read(buf, BUF_SIZE, &err)) > 0)
133 			size += res;
134 	} else {
135 		ih = (struct image_header *)buf;
136 		ret = spl_parse_image_header(spl_image, ih);
137 		if (ret)
138 			goto end_stream;
139 #ifdef CONFIG_SPL_GZIP
140 		if (ih->ih_comp == IH_COMP_GZIP)
141 			addr = CONFIG_SYS_LOAD_ADDR;
142 		else
143 #endif
144 			addr = spl_image->load_addr;
145 		memcpy((void *)addr, buf, res);
146 		ih = (struct image_header *)addr;
147 		size += res;
148 		addr += res;
149 
150 		while ((res = xyzModem_stream_read(buf, BUF_SIZE, &err)) > 0) {
151 			memcpy((void *)addr, buf, res);
152 			size += res;
153 			addr += res;
154 		}
155 	}
156 
157 end_stream:
158 	xyzModem_stream_close(&err);
159 	xyzModem_stream_terminate(false, &getcymodem);
160 
161 	printf("Loaded %lu bytes\n", size);
162 
163 #ifdef CONFIG_SPL_GZIP
164 	if (!(IS_ENABLED(CONFIG_SPL_LOAD_FIT) &&
165 	      image_get_magic((struct image_header *)buf) == FDT_MAGIC) &&
166 	    (ih->ih_comp == IH_COMP_GZIP)) {
167 		if (gunzip((void *)(spl_image->load_addr + sizeof(*ih)),
168 			   CONFIG_SYS_BOOTM_LEN,
169 			   (void *)(CONFIG_SYS_LOAD_ADDR + sizeof(*ih)),
170 			   &size)) {
171 			puts("Uncompressing error\n");
172 			return -EIO;
173 		}
174 	}
175 #endif
176 
177 	return ret;
178 }
179 SPL_LOAD_IMAGE_METHOD("UART", 0, BOOT_DEVICE_UART, spl_ymodem_load_image);
180