blob: a175533ada9d5562b396caecc370cdc476fa7f78 [file] [log] [blame]
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * WGET/HTTP support driver based on U-BOOT's nfs.c
4 * Copyright Duncan Hare <dh@synoia.com> 2017
5 */
6
Masahisa Kojima77b0ae32023-11-10 13:25:34 +09007#include <asm/global_data.h>
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +08008#include <command.h>
Michael Wallecaad55b2022-12-28 16:27:14 +01009#include <display_options.h>
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080010#include <env.h>
Jerome Forissier612b2b12024-09-11 11:58:22 +020011#include <efi_loader.h>
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080012#include <image.h>
Masahisa Kojima77b0ae32023-11-10 13:25:34 +090013#include <lmb.h>
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080014#include <mapmem.h>
15#include <net.h>
16#include <net/tcp.h>
17#include <net/wget.h>
Masahisa Kojima6721d182023-11-10 13:25:35 +090018#include <stdlib.h>
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080019
Masahisa Kojima77b0ae32023-11-10 13:25:34 +090020DECLARE_GLOBAL_DATA_PTR;
21
Marek Vasut22a95082023-12-13 22:11:13 +010022/* The default, change with environment variable 'httpdstp' */
23#define SERVER_PORT 80
24
Adriano Cordova47f35e32024-11-11 18:08:58 -030025static const char bootfileGET[] = "GET ";
26static const char bootfileHEAD[] = "HEAD ";
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080027static const char bootfile3[] = " HTTP/1.0\r\n\r\n";
28static const char http_eom[] = "\r\n\r\n";
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080029static const char content_len[] = "Content-Length";
30static const char linefeed[] = "\r\n";
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080031static int wget_timeout_count;
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +030032struct tcp_stream *tcp;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080033
34struct pkt_qd {
35 uchar *pkt;
36 unsigned int tcp_seq_num;
37 unsigned int len;
38};
39
40/*
41 * This is a control structure for out of order packets received.
42 * The actual packet bufers are in the kernel space, and are
43 * expected to be overwritten by the downloaded image.
44 */
Richard Weinbergerbf44cf42023-07-20 14:51:56 +020045#define PKTQ_SZ (PKTBUFSRX / 4)
46static struct pkt_qd pkt_q[PKTQ_SZ];
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080047static int pkt_q_idx;
48static unsigned long content_length;
49static unsigned int packets;
50
51static unsigned int initial_data_seq_num;
Yasuharu Shibata07a00ef2024-04-14 19:46:07 +090052static unsigned int next_data_seq_num;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080053
54static enum wget_state current_wget_state;
55
56static char *image_url;
57static unsigned int wget_timeout = WGET_TIMEOUT;
58
59static enum net_loop_state wget_loop_state;
60
61/* Timeout retry parameters */
62static u8 retry_action; /* actions for TCP retry */
63static unsigned int retry_tcp_ack_num; /* TCP retry acknowledge number*/
64static unsigned int retry_tcp_seq_num; /* TCP retry sequence number */
65static int retry_len; /* TCP retry length */
66
67/**
68 * store_block() - store block in memory
69 * @src: source of data
70 * @offset: offset
71 * @len: length
72 */
73static inline int store_block(uchar *src, unsigned int offset, unsigned int len)
74{
Masahisa Kojima77b0ae32023-11-10 13:25:34 +090075 ulong store_addr = image_load_addr + offset;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080076 ulong newsize = offset + len;
77 uchar *ptr;
78
Adriano Cordova47f35e32024-11-11 18:08:58 -030079 if (CONFIG_IS_ENABLED(LMB) && wget_info->set_bootdev) {
Masahisa Kojima77b0ae32023-11-10 13:25:34 +090080 if (store_addr < image_load_addr ||
Sughosh Ganu77728092024-09-16 20:50:25 +053081 lmb_read_check(store_addr, len)) {
Masahisa Kojima77b0ae32023-11-10 13:25:34 +090082 printf("\nwget error: ");
83 printf("trying to overwrite reserved memory...\n");
84 return -1;
85 }
86 }
87
88 ptr = map_sysmem(store_addr, len);
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080089 memcpy(ptr, src, len);
90 unmap_sysmem(ptr);
91
92 if (net_boot_file_size < (offset + len))
93 net_boot_file_size = newsize;
94
95 return 0;
96}
97
98/**
99 * wget_send_stored() - wget response dispatcher
100 *
101 * WARNING, This, and only this, is the place in wget.c where
102 * SEQUENCE NUMBERS are swapped between incoming (RX)
103 * and outgoing (TX).
104 * Procedure wget_handler() is correct for RX traffic.
105 */
106static void wget_send_stored(void)
107{
108 u8 action = retry_action;
109 int len = retry_len;
Dmitrii Merkurev3acd0542023-04-12 19:49:29 +0100110 unsigned int tcp_ack_num = retry_tcp_seq_num + (len == 0 ? 1 : len);
111 unsigned int tcp_seq_num = retry_tcp_ack_num;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800112 uchar *ptr, *offset;
113
114 switch (current_wget_state) {
115 case WGET_CLOSED:
116 debug_cond(DEBUG_WGET, "wget: send SYN\n");
117 current_wget_state = WGET_CONNECTING;
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300118 net_send_tcp_packet(0, tcp->rhost, tcp->rport, tcp->lport, action,
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800119 tcp_seq_num, tcp_ack_num);
120 packets = 0;
121 break;
122 case WGET_CONNECTING:
123 pkt_q_idx = 0;
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300124 net_send_tcp_packet(0, tcp->rhost, tcp->rport, tcp->lport, action,
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800125 tcp_seq_num, tcp_ack_num);
126
127 ptr = net_tx_packet + net_eth_hdr_size() +
128 IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2;
129 offset = ptr;
130
Adriano Cordova47f35e32024-11-11 18:08:58 -0300131 switch (wget_info->method) {
132 case WGET_HTTP_METHOD_HEAD:
133 memcpy(offset, &bootfileHEAD, strlen(bootfileHEAD));
134 offset += strlen(bootfileHEAD);
135 break;
136 case WGET_HTTP_METHOD_GET:
137 default:
138 memcpy(offset, &bootfileGET, strlen(bootfileGET));
139 offset += strlen(bootfileGET);
140 break;
141 }
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800142
143 memcpy(offset, image_url, strlen(image_url));
144 offset += strlen(image_url);
145
146 memcpy(offset, &bootfile3, strlen(bootfile3));
147 offset += strlen(bootfile3);
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300148 net_send_tcp_packet((offset - ptr), tcp->rhost, tcp->rport, tcp->lport,
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800149 TCP_PUSH, tcp_seq_num, tcp_ack_num);
150 current_wget_state = WGET_CONNECTED;
151 break;
152 case WGET_CONNECTED:
153 case WGET_TRANSFERRING:
154 case WGET_TRANSFERRED:
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300155 net_send_tcp_packet(0, tcp->rhost, tcp->rport, tcp->lport, action,
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800156 tcp_seq_num, tcp_ack_num);
157 break;
158 }
159}
160
Dmitrii Merkurev3acd0542023-04-12 19:49:29 +0100161static void wget_send(u8 action, unsigned int tcp_seq_num,
162 unsigned int tcp_ack_num, int len)
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800163{
164 retry_action = action;
165 retry_tcp_ack_num = tcp_ack_num;
166 retry_tcp_seq_num = tcp_seq_num;
167 retry_len = len;
168
169 wget_send_stored();
170}
171
172void wget_fail(char *error_message, unsigned int tcp_seq_num,
173 unsigned int tcp_ack_num, u8 action)
174{
175 printf("wget: Transfer Fail - %s\n", error_message);
176 net_set_timeout_handler(0, NULL);
177 wget_send(action, tcp_seq_num, tcp_ack_num, 0);
178}
179
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800180/*
181 * Interfaces of U-BOOT
182 */
183static void wget_timeout_handler(void)
184{
185 if (++wget_timeout_count > WGET_RETRY_COUNT) {
186 puts("\nRetry count exceeded; starting again\n");
187 wget_send(TCP_RST, 0, 0, 0);
188 net_start_again();
189 } else {
190 puts("T ");
191 net_set_timeout_handler(wget_timeout +
192 WGET_TIMEOUT * wget_timeout_count,
193 wget_timeout_handler);
194 wget_send_stored();
195 }
196}
197
198#define PKT_QUEUE_OFFSET 0x20000
199#define PKT_QUEUE_PACKET_SIZE 0x800
200
Adriano Cordova47f35e32024-11-11 18:08:58 -0300201static void wget_fill_info(const uchar *pkt, int hlen)
202{
203 const char *first_space;
204 const char *second_space;
205 char *pos, *end;
206
Heinrich Schuchardt89e40902024-11-26 13:19:20 -0300207 if (wget_info->headers) {
208 if (hlen < MAX_HTTP_HEADERS_SIZE)
209 strncpy(wget_info->headers, pkt, hlen);
210 else
211 hlen = 0;
212 wget_info->headers[hlen] = 0;
213 }
Adriano Cordova47f35e32024-11-11 18:08:58 -0300214
215 //Get status code
216 first_space = strchr(pkt, ' ');
217 if (!first_space) {
218 wget_info->status_code = -1;
219 return;
220 }
221
222 second_space = strchr(first_space + 1, ' ');
223 if (!second_space) {
224 wget_info->status_code = -1;
225 return;
226 }
227
228 wget_info->status_code = (u32)simple_strtoul(first_space + 1, &end, 10);
229
230 if (second_space != end)
231 wget_info->status_code = -1;
232
233 pos = strstr((char *)pkt, content_len);
234
235 if (pos) {
236 pos += sizeof(content_len) + 1;
237 while (*pos == ' ')
238 pos++;
239 content_length = simple_strtoul(pos, &end, 10);
240 debug_cond(DEBUG_WGET,
241 "wget: Connected Len %lu\n",
242 content_length);
243 wget_info->hdr_cont_len = content_length;
244 }
245}
246
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800247static void wget_connected(uchar *pkt, unsigned int tcp_seq_num,
Dmitrii Merkurev3acd0542023-04-12 19:49:29 +0100248 u8 action, unsigned int tcp_ack_num, unsigned int len)
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800249{
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800250 uchar *pkt_in_q;
251 char *pos;
252 int hlen, i;
253 uchar *ptr1;
254
255 pkt[len] = '\0';
256 pos = strstr((char *)pkt, http_eom);
257
258 if (!pos) {
259 debug_cond(DEBUG_WGET,
260 "wget: Connected, data before Header %p\n", pkt);
261 pkt_in_q = (void *)image_load_addr + PKT_QUEUE_OFFSET +
262 (pkt_q_idx * PKT_QUEUE_PACKET_SIZE);
263
Yasuharu Shibataafab2682024-08-14 21:41:06 +0900264 ptr1 = map_sysmem((ulong)pkt_in_q, len);
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800265 memcpy(ptr1, pkt, len);
266 unmap_sysmem(ptr1);
267
268 pkt_q[pkt_q_idx].pkt = pkt_in_q;
269 pkt_q[pkt_q_idx].tcp_seq_num = tcp_seq_num;
270 pkt_q[pkt_q_idx].len = len;
271 pkt_q_idx++;
Richard Weinbergerbf44cf42023-07-20 14:51:56 +0200272
273 if (pkt_q_idx >= PKTQ_SZ) {
274 printf("wget: Fatal error, queue overrun!\n");
275 net_set_state(NETLOOP_FAIL);
276
277 return;
278 }
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800279 } else {
280 debug_cond(DEBUG_WGET, "wget: Connected HTTP Header %p\n", pkt);
281 /* sizeof(http_eom) - 1 is the string length of (http_eom) */
282 hlen = pos - (char *)pkt + sizeof(http_eom) - 1;
283 pos = strstr((char *)pkt, linefeed);
284 if (pos > 0)
285 i = pos - (char *)pkt;
286 else
287 i = hlen;
288 printf("%.*s", i, pkt);
289
290 current_wget_state = WGET_TRANSFERRING;
291
Yasuharu Shibata07a00ef2024-04-14 19:46:07 +0900292 initial_data_seq_num = tcp_seq_num + hlen;
293 next_data_seq_num = tcp_seq_num + len;
294
Adriano Cordova47f35e32024-11-11 18:08:58 -0300295 wget_fill_info(pkt, hlen);
296 debug_cond(DEBUG_WGET,
297 "wget: HTTP Status Code %d\n", wget_info->status_code);
298
299 if (wget_info->status_code != 200) {
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800300 debug_cond(DEBUG_WGET,
301 "wget: Connected Bad Xfer\n");
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800302 wget_loop_state = NETLOOP_FAIL;
303 wget_send(action, tcp_seq_num, tcp_ack_num, len);
304 } else {
305 debug_cond(DEBUG_WGET,
Heinrich Schuchardt6b3cefc2024-10-09 01:02:05 +0200306 "wget: Connected Pkt %p hlen %x\n",
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800307 pkt, hlen);
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800308
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800309 net_boot_file_size = 0;
310
Masahisa Kojima77b0ae32023-11-10 13:25:34 +0900311 if (len > hlen) {
312 if (store_block(pkt + hlen, 0, len - hlen) != 0) {
313 wget_loop_state = NETLOOP_FAIL;
314 wget_fail("wget: store error\n", tcp_seq_num, tcp_ack_num, action);
315 net_set_state(NETLOOP_FAIL);
316 return;
317 }
318 }
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800319
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800320 for (i = 0; i < pkt_q_idx; i++) {
Masahisa Kojima77b0ae32023-11-10 13:25:34 +0900321 int err;
322
Yasuharu Shibataafab2682024-08-14 21:41:06 +0900323 ptr1 = map_sysmem((ulong)pkt_q[i].pkt,
324 pkt_q[i].len);
Masahisa Kojima77b0ae32023-11-10 13:25:34 +0900325 err = store_block(ptr1,
326 pkt_q[i].tcp_seq_num -
327 initial_data_seq_num,
328 pkt_q[i].len);
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800329 unmap_sysmem(ptr1);
330 debug_cond(DEBUG_WGET,
Heinrich Schuchardt6b3cefc2024-10-09 01:02:05 +0200331 "wget: Conncted pkt Q %p len %x\n",
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800332 pkt_q[i].pkt, pkt_q[i].len);
Masahisa Kojima77b0ae32023-11-10 13:25:34 +0900333 if (err) {
334 wget_loop_state = NETLOOP_FAIL;
335 wget_fail("wget: store error\n", tcp_seq_num, tcp_ack_num, action);
336 net_set_state(NETLOOP_FAIL);
337 return;
338 }
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800339 }
340 }
341 }
342 wget_send(action, tcp_seq_num, tcp_ack_num, len);
343}
344
345/**
Dmitrii Merkurev3acd0542023-04-12 19:49:29 +0100346 * wget_handler() - TCP handler of wget
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300347 * @tcp: TCP stream
Dmitrii Merkurev3acd0542023-04-12 19:49:29 +0100348 * @pkt: pointer to the application packet
Dmitrii Merkurev3acd0542023-04-12 19:49:29 +0100349 * @tcp_seq_num: TCP sequential number
350 * @tcp_ack_num: TCP acknowledgment number
351 * @action: TCP action (SYN, ACK, FIN, etc)
352 * @len: packet length
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800353 *
354 * In the "application push" invocation, the TCP header with all
355 * its information is pointed to by the packet pointer.
356 */
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300357static void wget_handler(struct tcp_stream *tcp, uchar *pkt,
Dmitrii Merkurev3acd0542023-04-12 19:49:29 +0100358 u32 tcp_seq_num, u32 tcp_ack_num,
359 u8 action, unsigned int len)
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800360{
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300361 enum tcp_state wget_tcp_state = tcp_stream_get_state(tcp);
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800362
363 net_set_timeout_handler(wget_timeout, wget_timeout_handler);
364 packets++;
365
366 switch (current_wget_state) {
367 case WGET_CLOSED:
368 debug_cond(DEBUG_WGET, "wget: Handler: Error!, State wrong\n");
369 break;
370 case WGET_CONNECTING:
371 debug_cond(DEBUG_WGET,
Dmitrii Merkurev3acd0542023-04-12 19:49:29 +0100372 "wget: Connecting In len=%x, Seq=%u, Ack=%u\n",
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800373 len, tcp_seq_num, tcp_ack_num);
374 if (!len) {
375 if (wget_tcp_state == TCP_ESTABLISHED) {
376 debug_cond(DEBUG_WGET,
377 "wget: Cting, send, len=%x\n", len);
378 wget_send(action, tcp_seq_num, tcp_ack_num,
379 len);
380 } else {
381 printf("%.*s", len, pkt);
382 wget_fail("wget: Handler Connected Fail\n",
383 tcp_seq_num, tcp_ack_num, action);
384 }
385 }
386 break;
387 case WGET_CONNECTED:
Dmitrii Merkurev3acd0542023-04-12 19:49:29 +0100388 debug_cond(DEBUG_WGET, "wget: Connected seq=%u, len=%x\n",
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800389 tcp_seq_num, len);
390 if (!len) {
391 wget_fail("Image not found, no data returned\n",
392 tcp_seq_num, tcp_ack_num, action);
393 } else {
Dmitrii Merkurev3acd0542023-04-12 19:49:29 +0100394 wget_connected(pkt, tcp_seq_num, action, tcp_ack_num, len);
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800395 }
396 break;
397 case WGET_TRANSFERRING:
398 debug_cond(DEBUG_WGET,
399 "wget: Transferring, seq=%x, ack=%x,len=%x\n",
400 tcp_seq_num, tcp_ack_num, len);
401
Yasuharu Shibata07a00ef2024-04-14 19:46:07 +0900402 if (next_data_seq_num != tcp_seq_num) {
403 debug_cond(DEBUG_WGET, "wget: seq=%x packet was lost\n", next_data_seq_num);
404 return;
405 }
406 next_data_seq_num = tcp_seq_num + len;
407
Yasuharu Shibata2c9b5af2024-04-16 09:26:24 +0900408 if (store_block(pkt, tcp_seq_num - initial_data_seq_num, len) != 0) {
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800409 wget_fail("wget: store error\n",
410 tcp_seq_num, tcp_ack_num, action);
Masahisa Kojima77b0ae32023-11-10 13:25:34 +0900411 net_set_state(NETLOOP_FAIL);
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800412 return;
413 }
414
415 switch (wget_tcp_state) {
416 case TCP_FIN_WAIT_2:
417 wget_send(TCP_ACK, tcp_seq_num, tcp_ack_num, len);
418 fallthrough;
419 case TCP_SYN_SENT:
Dmitrii Merkurev3acd0542023-04-12 19:49:29 +0100420 case TCP_SYN_RECEIVED:
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800421 case TCP_CLOSING:
422 case TCP_FIN_WAIT_1:
423 case TCP_CLOSED:
424 net_set_state(NETLOOP_FAIL);
425 break;
426 case TCP_ESTABLISHED:
427 wget_send(TCP_ACK, tcp_seq_num, tcp_ack_num,
428 len);
429 wget_loop_state = NETLOOP_SUCCESS;
430 break;
431 case TCP_CLOSE_WAIT: /* End of transfer */
432 current_wget_state = WGET_TRANSFERRED;
433 wget_send(action | TCP_ACK | TCP_FIN,
434 tcp_seq_num, tcp_ack_num, len);
435 break;
436 }
437 break;
438 case WGET_TRANSFERRED:
439 printf("Packets received %d, Transfer Successful\n", packets);
440 net_set_state(wget_loop_state);
Adriano Cordova47f35e32024-11-11 18:08:58 -0300441 wget_info->file_size = net_boot_file_size;
442 if (wget_info->method == WGET_HTTP_METHOD_GET && wget_info->set_bootdev) {
Adriano Cordova93cba0f2024-12-04 00:05:23 -0300443 efi_set_bootdev("Http", NULL, image_url,
Adriano Cordova47f35e32024-11-11 18:08:58 -0300444 map_sysmem(image_load_addr, 0),
445 net_boot_file_size);
446 env_set_hex("filesize", net_boot_file_size);
447 }
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800448 break;
449 }
450}
451
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800452#define BLOCKSIZE 512
453
454void wget_start(void)
455{
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300456 struct in_addr web_server_ip;
457 unsigned int server_port;
458
Adriano Cordova47f35e32024-11-11 18:08:58 -0300459 if (!wget_info)
460 wget_info = &default_wget_info;
461
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800462 image_url = strchr(net_boot_file_name, ':');
463 if (image_url > 0) {
464 web_server_ip = string_to_ip(net_boot_file_name);
465 ++image_url;
466 net_server_ip = web_server_ip;
467 } else {
468 web_server_ip = net_server_ip;
469 image_url = net_boot_file_name;
470 }
471
472 debug_cond(DEBUG_WGET,
473 "wget: Transfer HTTP Server %pI4; our IP %pI4\n",
474 &web_server_ip, &net_ip);
475
476 /* Check if we need to send across this subnet */
477 if (net_gateway.s_addr && net_netmask.s_addr) {
478 struct in_addr our_net;
479 struct in_addr server_net;
480
481 our_net.s_addr = net_ip.s_addr & net_netmask.s_addr;
482 server_net.s_addr = net_server_ip.s_addr & net_netmask.s_addr;
483 if (our_net.s_addr != server_net.s_addr)
484 debug_cond(DEBUG_WGET,
485 "wget: sending through gateway %pI4",
486 &net_gateway);
487 }
488 debug_cond(DEBUG_WGET, "URL '%s'\n", image_url);
489
490 if (net_boot_file_expected_size_in_blocks) {
491 debug_cond(DEBUG_WGET, "wget: Size is 0x%x Bytes = ",
492 net_boot_file_expected_size_in_blocks * BLOCKSIZE);
493 print_size(net_boot_file_expected_size_in_blocks * BLOCKSIZE,
494 "");
495 }
496 debug_cond(DEBUG_WGET,
497 "\nwget:Load address: 0x%lx\nLoading: *\b", image_load_addr);
498
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800499 net_set_timeout_handler(wget_timeout, wget_timeout_handler);
500 tcp_set_tcp_handler(wget_handler);
501
502 wget_timeout_count = 0;
503 current_wget_state = WGET_CLOSED;
504
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800505 /*
506 * Zero out server ether to force arp resolution in case
507 * the server ip for the previous u-boot command, for example dns
508 * is not the same as the web server ip.
509 */
510
511 memset(net_server_ethaddr, 0, 6);
512
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300513 server_port = env_get_ulong("httpdstp", 10, SERVER_PORT) & 0xffff;
514 tcp = tcp_stream_connect(web_server_ip, server_port);
515 if (!tcp) {
516 net_set_state(NETLOOP_FAIL);
517 return;
518 }
519
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800520 wget_send(TCP_SYN, 0, 0, 0);
521}
Masahisa Kojima6721d182023-11-10 13:25:35 +0900522
Adriano Cordovab479fc42024-12-04 00:05:16 -0300523int wget_do_request(ulong dst_addr, char *uri)
Masahisa Kojima6721d182023-11-10 13:25:35 +0900524{
525 int ret;
526 char *s, *host_name, *file_name, *str_copy;
527
528 /*
529 * Download file using wget.
530 *
531 * U-Boot wget takes the target uri in this format.
532 * "<http server ip>:<file path>" e.g.) 192.168.1.1:/sample/test.iso
533 * Need to resolve the http server ip address before starting wget.
534 */
535 str_copy = strdup(uri);
536 if (!str_copy)
537 return -ENOMEM;
538
539 s = str_copy + strlen("http://");
540 host_name = strsep(&s, "/");
541 if (!s) {
Masahisa Kojima6721d182023-11-10 13:25:35 +0900542 ret = -EINVAL;
543 goto out;
544 }
545 file_name = s;
546
Adriano Cordovab479fc42024-12-04 00:05:16 -0300547 host_name = strsep(&host_name, ":");
548
549 if (string_to_ip(host_name).s_addr) {
550 s = host_name;
551 } else {
552#if IS_ENABLED(CONFIG_CMD_DNS)
553 net_dns_resolve = host_name;
554 net_dns_env_var = "httpserverip";
555 if (net_loop(DNS) < 0) {
556 ret = -EINVAL;
557 goto out;
558 }
559 s = env_get("httpserverip");
560 if (!s) {
561 ret = -EINVAL;
562 goto out;
563 }
564#else
Masahisa Kojima6721d182023-11-10 13:25:35 +0900565 ret = -EINVAL;
566 goto out;
Adriano Cordovab479fc42024-12-04 00:05:16 -0300567#endif
Masahisa Kojima6721d182023-11-10 13:25:35 +0900568 }
569
570 strlcpy(net_boot_file_name, s, sizeof(net_boot_file_name));
571 strlcat(net_boot_file_name, ":/", sizeof(net_boot_file_name)); /* append '/' which is removed by strsep() */
572 strlcat(net_boot_file_name, file_name, sizeof(net_boot_file_name));
573 image_load_addr = dst_addr;
574 ret = net_loop(WGET);
575
576out:
577 free(str_copy);
578
Adriano Cordova450b2342024-11-11 18:08:59 -0300579 return ret < 0 ? ret : 0;
Masahisa Kojima6721d182023-11-10 13:25:35 +0900580}
Masahisa Kojimae501ce12023-11-10 13:25:41 +0900581
582/**
583 * wget_validate_uri() - validate the uri for wget
584 *
585 * @uri: uri string
586 *
587 * This function follows the current U-Boot wget implementation.
588 * scheme: only "http:" is supported
589 * authority:
590 * - user information: not supported
591 * - host: supported
592 * - port: not supported(always use the default port)
593 *
594 * Uri is expected to be correctly percent encoded.
595 * This is the minimum check, control codes(0x1-0x19, 0x7F, except '\0')
596 * and space character(0x20) are not allowed.
597 *
598 * TODO: stricter uri conformance check
599 *
600 * Return: true on success, false on failure
601 */
602bool wget_validate_uri(char *uri)
603{
604 char c;
605 bool ret = true;
606 char *str_copy, *s, *authority;
607
608 for (c = 0x1; c < 0x21; c++) {
609 if (strchr(uri, c)) {
610 log_err("invalid character is used\n");
611 return false;
612 }
613 }
614 if (strchr(uri, 0x7f)) {
615 log_err("invalid character is used\n");
616 return false;
617 }
618
619 if (strncmp(uri, "http://", 7)) {
620 log_err("only http:// is supported\n");
621 return false;
622 }
623 str_copy = strdup(uri);
624 if (!str_copy)
625 return false;
626
627 s = str_copy + strlen("http://");
628 authority = strsep(&s, "/");
629 if (!s) {
630 log_err("invalid uri, no file path\n");
631 ret = false;
632 goto out;
633 }
634 s = strchr(authority, '@');
635 if (s) {
636 log_err("user information is not supported\n");
637 ret = false;
638 goto out;
639 }
640 s = strchr(authority, ':');
641 if (s) {
642 log_err("user defined port is not supported\n");
643 ret = false;
644 goto out;
645 }
646
647out:
648 free(str_copy);
649
650 return ret;
651}