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