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