1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (C) 2023 The Android Open Source Project
4  */
5 
6 #include <common.h>
7 #include <fastboot.h>
8 #include <net.h>
9 #include <net/fastboot_tcp.h>
10 #include <net/tcp.h>
11 
12 static char command[FASTBOOT_COMMAND_LEN] = {0};
13 static char response[FASTBOOT_RESPONSE_LEN] = {0};
14 
15 static const unsigned short handshake_length = 4;
16 static const uchar *handshake = "FB01";
17 
18 static u16 curr_sport;
19 static u16 curr_dport;
20 static u32 curr_tcp_seq_num;
21 static u32 curr_tcp_ack_num;
22 static unsigned int curr_request_len;
23 static enum fastboot_tcp_state {
24 	FASTBOOT_CLOSED,
25 	FASTBOOT_CONNECTED,
26 	FASTBOOT_DISCONNECTING
27 } state = FASTBOOT_CLOSED;
28 
fastboot_tcp_answer(u8 action,unsigned int len)29 static void fastboot_tcp_answer(u8 action, unsigned int len)
30 {
31 	const u32 response_seq_num = curr_tcp_ack_num;
32 	const u32 response_ack_num = curr_tcp_seq_num +
33 		  (curr_request_len > 0 ? curr_request_len : 1);
34 
35 	net_send_tcp_packet(len, htons(curr_sport), htons(curr_dport),
36 			    action, response_seq_num, response_ack_num);
37 }
38 
fastboot_tcp_reset(void)39 static void fastboot_tcp_reset(void)
40 {
41 	fastboot_tcp_answer(TCP_RST, 0);
42 	state = FASTBOOT_CLOSED;
43 }
44 
fastboot_tcp_send_packet(u8 action,const uchar * data,unsigned int len)45 static void fastboot_tcp_send_packet(u8 action, const uchar *data, unsigned int len)
46 {
47 	uchar *pkt = net_get_async_tx_pkt_buf();
48 
49 	memset(pkt, '\0', PKTSIZE);
50 	pkt += net_eth_hdr_size() + IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2;
51 	memcpy(pkt, data, len);
52 	fastboot_tcp_answer(action, len);
53 	memset(pkt, '\0', PKTSIZE);
54 }
55 
fastboot_tcp_send_message(const char * message,unsigned int len)56 static void fastboot_tcp_send_message(const char *message, unsigned int len)
57 {
58 	__be64 len_be = __cpu_to_be64(len);
59 	uchar *pkt = net_get_async_tx_pkt_buf();
60 
61 	memset(pkt, '\0', PKTSIZE);
62 	pkt += net_eth_hdr_size() + IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2;
63 	// Put first 8 bytes as a big endian message length
64 	memcpy(pkt, &len_be, 8);
65 	pkt += 8;
66 	memcpy(pkt, message, len);
67 	fastboot_tcp_answer(TCP_ACK | TCP_PUSH, len + 8);
68 	memset(pkt, '\0', PKTSIZE);
69 }
70 
fastboot_tcp_handler_ipv4(uchar * pkt,u16 dport,struct in_addr sip,u16 sport,u32 tcp_seq_num,u32 tcp_ack_num,u8 action,unsigned int len)71 static void fastboot_tcp_handler_ipv4(uchar *pkt, u16 dport,
72 				      struct in_addr sip, u16 sport,
73 				      u32 tcp_seq_num, u32 tcp_ack_num,
74 				      u8 action, unsigned int len)
75 {
76 	int fastboot_command_id;
77 	u64 command_size;
78 	u8 tcp_fin = action & TCP_FIN;
79 	u8 tcp_push = action & TCP_PUSH;
80 
81 	curr_sport = sport;
82 	curr_dport = dport;
83 	curr_tcp_seq_num = tcp_seq_num;
84 	curr_tcp_ack_num = tcp_ack_num;
85 	curr_request_len = len;
86 
87 	switch (state) {
88 	case FASTBOOT_CLOSED:
89 		if (tcp_push) {
90 			if (len != handshake_length ||
91 			    strlen(pkt) != handshake_length ||
92 			    memcmp(pkt, handshake, handshake_length) != 0) {
93 				fastboot_tcp_reset();
94 				break;
95 			}
96 			fastboot_tcp_send_packet(TCP_ACK | TCP_PUSH,
97 						 handshake, handshake_length);
98 			state = FASTBOOT_CONNECTED;
99 		}
100 		break;
101 	case FASTBOOT_CONNECTED:
102 		if (tcp_fin) {
103 			fastboot_tcp_answer(TCP_FIN | TCP_ACK, 0);
104 			state = FASTBOOT_DISCONNECTING;
105 			break;
106 		}
107 		if (tcp_push) {
108 			// First 8 bytes is big endian message length
109 			command_size = __be64_to_cpu(*(u64 *)pkt);
110 			len -= 8;
111 			pkt += 8;
112 
113 			// Only single packet messages are supported ATM
114 			if (strlen(pkt) != command_size) {
115 				fastboot_tcp_reset();
116 				break;
117 			}
118 			strlcpy(command, pkt, len + 1);
119 			fastboot_command_id = fastboot_handle_command(command, response);
120 			fastboot_tcp_send_message(response, strlen(response));
121 			fastboot_handle_boot(fastboot_command_id,
122 					     strncmp("OKAY", response, 4) == 0);
123 		}
124 		break;
125 	case FASTBOOT_DISCONNECTING:
126 		if (tcp_push)
127 			state = FASTBOOT_CLOSED;
128 		break;
129 	}
130 
131 	memset(command, 0, FASTBOOT_COMMAND_LEN);
132 	memset(response, 0, FASTBOOT_RESPONSE_LEN);
133 	curr_sport = 0;
134 	curr_dport = 0;
135 	curr_tcp_seq_num = 0;
136 	curr_tcp_ack_num = 0;
137 	curr_request_len = 0;
138 }
139 
fastboot_tcp_start_server(void)140 void fastboot_tcp_start_server(void)
141 {
142 	printf("Using %s device\n", eth_get_name());
143 	printf("Listening for fastboot command on tcp %pI4\n", &net_ip);
144 
145 	tcp_set_tcp_handler(fastboot_tcp_handler_ipv4);
146 }
147