tools: kwbimage: Update comments describing kwbimage v1 structures
[platform/kernel/u-boot.git] / net / fastboot.c
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (C) 2016 The Android Open Source Project
4  */
5
6 #include <common.h>
7 #include <command.h>
8 #include <fastboot.h>
9 #include <net.h>
10 #include <net/fastboot.h>
11
12 /* Fastboot port # defined in spec */
13 #define WELL_KNOWN_PORT 5554
14
15 enum {
16         FASTBOOT_ERROR = 0,
17         FASTBOOT_QUERY = 1,
18         FASTBOOT_INIT = 2,
19         FASTBOOT_FASTBOOT = 3,
20 };
21
22 struct __packed fastboot_header {
23         uchar id;
24         uchar flags;
25         unsigned short seq;
26 };
27
28 #define PACKET_SIZE 1024
29 #define DATA_SIZE (PACKET_SIZE - sizeof(struct fastboot_header))
30
31 /* Sequence number sent for every packet */
32 static unsigned short sequence_number = 1;
33 static const unsigned short packet_size = PACKET_SIZE;
34 static const unsigned short udp_version = 1;
35
36 /* Keep track of last packet for resubmission */
37 static uchar last_packet[PACKET_SIZE];
38 static unsigned int last_packet_len;
39
40 static struct in_addr fastboot_remote_ip;
41 /* The UDP port at their end */
42 static int fastboot_remote_port;
43 /* The UDP port at our end */
44 static int fastboot_our_port;
45
46 static void boot_downloaded_image(void);
47
48 #if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
49 /**
50  * fastboot_udp_send_info() - Send an INFO packet during long commands.
51  *
52  * @msg: String describing the reason for waiting
53  */
54 static void fastboot_udp_send_info(const char *msg)
55 {
56         uchar *packet;
57         uchar *packet_base;
58         int len = 0;
59         char response[FASTBOOT_RESPONSE_LEN] = {0};
60
61         struct fastboot_header response_header = {
62                 .id = FASTBOOT_FASTBOOT,
63                 .flags = 0,
64                 .seq = htons(sequence_number)
65         };
66         ++sequence_number;
67         packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
68         packet_base = packet;
69
70         /* Write headers */
71         memcpy(packet, &response_header, sizeof(response_header));
72         packet += sizeof(response_header);
73         /* Write response */
74         fastboot_response("INFO", response, "%s", msg);
75         memcpy(packet, response, strlen(response));
76         packet += strlen(response);
77
78         len = packet - packet_base;
79
80         /* Save packet for retransmitting */
81         last_packet_len = len;
82         memcpy(last_packet, packet_base, last_packet_len);
83
84         net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
85                             fastboot_remote_port, fastboot_our_port, len);
86 }
87
88 /**
89  * fastboot_timed_send_info() - Send INFO packet every 30 seconds
90  *
91  * @msg: String describing the reason for waiting
92  *
93  * Send an INFO packet during long commands based on timer. An INFO packet
94  * is sent if the time is 30 seconds after start. Else, noop.
95  */
96 static void fastboot_timed_send_info(const char *msg)
97 {
98         static ulong start;
99
100         /* Initialize timer */
101         if (start == 0)
102                 start = get_timer(0);
103         ulong time = get_timer(start);
104         /* Send INFO packet to host every 30 seconds */
105         if (time >= 30000) {
106                 start = get_timer(0);
107                 fastboot_udp_send_info(msg);
108         }
109 }
110 #endif
111
112 /**
113  * fastboot_send() - Sends a packet in response to received fastboot packet
114  *
115  * @header: Header for response packet
116  * @fastboot_data: Pointer to received fastboot data
117  * @fastboot_data_len: Length of received fastboot data
118  * @retransmit: Nonzero if sending last sent packet
119  */
120 static void fastboot_send(struct fastboot_header header, char *fastboot_data,
121                           unsigned int fastboot_data_len, uchar retransmit)
122 {
123         uchar *packet;
124         uchar *packet_base;
125         int len = 0;
126         const char *error_msg = "An error occurred.";
127         short tmp;
128         struct fastboot_header response_header = header;
129         static char command[FASTBOOT_COMMAND_LEN];
130         static int cmd = -1;
131         static bool pending_command;
132         char response[FASTBOOT_RESPONSE_LEN] = {0};
133
134         /*
135          * We will always be sending some sort of packet, so
136          * cobble together the packet headers now.
137          */
138         packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
139         packet_base = packet;
140
141         /* Resend last packet */
142         if (retransmit) {
143                 memcpy(packet, last_packet, last_packet_len);
144                 net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
145                                     fastboot_remote_port, fastboot_our_port,
146                                     last_packet_len);
147                 return;
148         }
149
150         response_header.seq = htons(response_header.seq);
151         memcpy(packet, &response_header, sizeof(response_header));
152         packet += sizeof(response_header);
153
154         switch (header.id) {
155         case FASTBOOT_QUERY:
156                 tmp = htons(sequence_number);
157                 memcpy(packet, &tmp, sizeof(tmp));
158                 packet += sizeof(tmp);
159                 break;
160         case FASTBOOT_INIT:
161                 tmp = htons(udp_version);
162                 memcpy(packet, &tmp, sizeof(tmp));
163                 packet += sizeof(tmp);
164                 tmp = htons(packet_size);
165                 memcpy(packet, &tmp, sizeof(tmp));
166                 packet += sizeof(tmp);
167                 break;
168         case FASTBOOT_ERROR:
169                 memcpy(packet, error_msg, strlen(error_msg));
170                 packet += strlen(error_msg);
171                 break;
172         case FASTBOOT_FASTBOOT:
173                 if (cmd == FASTBOOT_COMMAND_DOWNLOAD) {
174                         if (!fastboot_data_len && !fastboot_data_remaining()) {
175                                 fastboot_data_complete(response);
176                         } else {
177                                 fastboot_data_download(fastboot_data,
178                                                        fastboot_data_len,
179                                                        response);
180                         }
181                 } else if (!pending_command) {
182                         strlcpy(command, fastboot_data,
183                                 min((size_t)fastboot_data_len + 1,
184                                     sizeof(command)));
185                         pending_command = true;
186                 } else {
187                         cmd = fastboot_handle_command(command, response);
188                         pending_command = false;
189                 }
190                 /*
191                  * Sent some INFO packets, need to update sequence number in
192                  * header
193                  */
194                 if (header.seq != sequence_number) {
195                         response_header.seq = htons(sequence_number);
196                         memcpy(packet_base, &response_header,
197                                sizeof(response_header));
198                 }
199                 /* Write response to packet */
200                 memcpy(packet, response, strlen(response));
201                 packet += strlen(response);
202                 break;
203         default:
204                 pr_err("ID %d not implemented.\n", header.id);
205                 return;
206         }
207
208         len = packet - packet_base;
209
210         /* Save packet for retransmitting */
211         last_packet_len = len;
212         memcpy(last_packet, packet_base, last_packet_len);
213
214         net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
215                             fastboot_remote_port, fastboot_our_port, len);
216
217         /* Continue boot process after sending response */
218         if (!strncmp("OKAY", response, 4)) {
219                 switch (cmd) {
220                 case FASTBOOT_COMMAND_BOOT:
221                         boot_downloaded_image();
222                         break;
223
224                 case FASTBOOT_COMMAND_CONTINUE:
225                         net_set_state(NETLOOP_SUCCESS);
226                         break;
227
228                 case FASTBOOT_COMMAND_REBOOT:
229                 case FASTBOOT_COMMAND_REBOOT_BOOTLOADER:
230                 case FASTBOOT_COMMAND_REBOOT_FASTBOOTD:
231                 case FASTBOOT_COMMAND_REBOOT_RECOVERY:
232                         do_reset(NULL, 0, 0, NULL);
233                         break;
234                 }
235         }
236
237         if (!strncmp("OKAY", response, 4) || !strncmp("FAIL", response, 4))
238                 cmd = -1;
239 }
240
241 /**
242  * boot_downloaded_image() - Boots into downloaded image.
243  */
244 static void boot_downloaded_image(void)
245 {
246         fastboot_boot();
247         net_set_state(NETLOOP_SUCCESS);
248 }
249
250 /**
251  * fastboot_handler() - Incoming UDP packet handler.
252  *
253  * @packet: Pointer to incoming UDP packet
254  * @dport: Destination UDP port
255  * @sip: Source IP address
256  * @sport: Source UDP port
257  * @len: Packet length
258  */
259 static void fastboot_handler(uchar *packet, unsigned int dport,
260                              struct in_addr sip, unsigned int sport,
261                              unsigned int len)
262 {
263         struct fastboot_header header;
264         char fastboot_data[DATA_SIZE] = {0};
265         unsigned int fastboot_data_len = 0;
266
267         if (dport != fastboot_our_port)
268                 return;
269
270         fastboot_remote_ip = sip;
271         fastboot_remote_port = sport;
272
273         if (len < sizeof(struct fastboot_header) || len > PACKET_SIZE)
274                 return;
275         memcpy(&header, packet, sizeof(header));
276         header.flags = 0;
277         header.seq = ntohs(header.seq);
278         packet += sizeof(header);
279         len -= sizeof(header);
280
281         switch (header.id) {
282         case FASTBOOT_QUERY:
283                 fastboot_send(header, fastboot_data, 0, 0);
284                 break;
285         case FASTBOOT_INIT:
286         case FASTBOOT_FASTBOOT:
287                 fastboot_data_len = len;
288                 if (len > 0)
289                         memcpy(fastboot_data, packet, len);
290                 if (header.seq == sequence_number) {
291                         fastboot_send(header, fastboot_data,
292                                       fastboot_data_len, 0);
293                         sequence_number++;
294                 } else if (header.seq == sequence_number - 1) {
295                         /* Retransmit last sent packet */
296                         fastboot_send(header, fastboot_data,
297                                       fastboot_data_len, 1);
298                 }
299                 break;
300         default:
301                 pr_err("ID %d not implemented.\n", header.id);
302                 header.id = FASTBOOT_ERROR;
303                 fastboot_send(header, fastboot_data, 0, 0);
304                 break;
305         }
306 }
307
308 void fastboot_start_server(void)
309 {
310         printf("Using %s device\n", eth_get_name());
311         printf("Listening for fastboot command on %pI4\n", &net_ip);
312
313         fastboot_our_port = WELL_KNOWN_PORT;
314
315 #if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
316         fastboot_set_progress_callback(fastboot_timed_send_info);
317 #endif
318         net_set_udp_handler(fastboot_handler);
319
320         /* zero out server ether in case the server ip has changed */
321         memset(net_server_ethaddr, 0, 6);
322 }