1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (C) 2023 The Android Open Source Project
4  */
5 
6 #include <fastboot.h>
7 #include <net.h>
8 #include <net/fastboot_tcp.h>
9 #include <net/tcp.h>
10 
11 #define FASTBOOT_TCP_PORT	5554
12 
13 static const unsigned short handshake_length = 4;
14 static const uchar *handshake = "FB01";
15 
16 static char rxbuf[sizeof(u64) + FASTBOOT_COMMAND_LEN + 1];
17 static char txbuf[sizeof(u64) + FASTBOOT_RESPONSE_LEN + 1];
18 
19 static u32 data_read;
20 static u32 tx_last_offs, tx_last_len;
21 
tcp_stream_on_rcv_nxt_update(struct tcp_stream * tcp,u32 rx_bytes)22 static void tcp_stream_on_rcv_nxt_update(struct tcp_stream *tcp, u32 rx_bytes)
23 {
24 	u64	cmd_size;
25 	__be64	len_be;
26 	char	saved;
27 	int	fastboot_command_id, len;
28 
29 	if (!data_read && rx_bytes >= handshake_length) {
30 		if (memcmp(rxbuf, handshake, handshake_length)) {
31 			printf("fastboot: bad handshake\n");
32 			tcp_stream_close(tcp);
33 			return;
34 		}
35 
36 		tx_last_offs = 0;
37 		tx_last_len = handshake_length;
38 		memcpy(txbuf, handshake, handshake_length);
39 
40 		data_read += handshake_length;
41 		rx_bytes -= handshake_length;
42 		if (rx_bytes > 0)
43 			memmove(rxbuf, rxbuf + handshake_length, rx_bytes);
44 		return;
45 	}
46 
47 	if (rx_bytes < sizeof(u64))
48 		return;
49 
50 	memcpy(&cmd_size, rxbuf, sizeof(u64));
51 	cmd_size = __be64_to_cpu(cmd_size);
52 	if (rx_bytes < sizeof(u64) + cmd_size)
53 		return;
54 
55 	saved = rxbuf[sizeof(u64) + cmd_size];
56 	rxbuf[sizeof(u64) + cmd_size] = '\0';
57 	fastboot_command_id = fastboot_handle_command(rxbuf + sizeof(u64),
58 						      txbuf + sizeof(u64));
59 	fastboot_handle_boot(fastboot_command_id,
60 			     strncmp("OKAY", txbuf + sizeof(u64), 4) != 0);
61 	rxbuf[sizeof(u64) + cmd_size] = saved;
62 
63 	len = strlen(txbuf + sizeof(u64));
64 	len_be = __cpu_to_be64(len);
65 	memcpy(txbuf, &len_be, sizeof(u64));
66 
67 	tx_last_offs += tx_last_len;
68 	tx_last_len = len + sizeof(u64);
69 
70 	data_read += sizeof(u64) + cmd_size;
71 	rx_bytes -= sizeof(u64) + cmd_size;
72 	if (rx_bytes > 0)
73 		memmove(rxbuf, rxbuf + sizeof(u64) + cmd_size, rx_bytes);
74 }
75 
tcp_stream_rx(struct tcp_stream * tcp,u32 rx_offs,void * buf,int len)76 static int tcp_stream_rx(struct tcp_stream *tcp, u32 rx_offs, void *buf, int len)
77 {
78 	memcpy(rxbuf + rx_offs - data_read, buf, len);
79 
80 	return len;
81 }
82 
tcp_stream_tx(struct tcp_stream * tcp,u32 tx_offs,void * buf,int maxlen)83 static int tcp_stream_tx(struct tcp_stream *tcp, u32 tx_offs, void *buf, int maxlen)
84 {
85 	/* by design: tx_offs >= tx_last_offs */
86 	if (tx_offs >= tx_last_offs + tx_last_len)
87 		return 0;
88 
89 	maxlen = tx_last_offs + tx_last_len - tx_offs;
90 	memcpy(buf, txbuf + (tx_offs - tx_last_offs), maxlen);
91 
92 	return maxlen;
93 }
94 
tcp_stream_on_create(struct tcp_stream * tcp)95 static int tcp_stream_on_create(struct tcp_stream *tcp)
96 {
97 	if (tcp->lport != FASTBOOT_TCP_PORT)
98 		return 0;
99 
100 	data_read = 0;
101 	tx_last_offs = 0;
102 	tx_last_len = 0;
103 
104 	tcp->on_rcv_nxt_update = tcp_stream_on_rcv_nxt_update;
105 	tcp->rx = tcp_stream_rx;
106 	tcp->tx = tcp_stream_tx;
107 
108 	return 1;
109 }
110 
fastboot_tcp_start_server(void)111 void fastboot_tcp_start_server(void)
112 {
113 	memset(net_server_ethaddr, 0, 6);
114 	tcp_stream_set_on_create_handler(tcp_stream_on_create);
115 
116 	printf("Using %s device\n", eth_get_name());
117 	printf("Listening for fastboot command on tcp %pI4\n", &net_ip);
118 }
119