blob: 4d34fdc5a4503e42f1a9a5f031ad0bfdd4857a0d [file] [log] [blame]
Dmitrii Merkurev308252d2023-04-12 19:49:30 +01001// SPDX-License-Identifier: BSD-2-Clause
2/*
3 * Copyright (C) 2023 The Android Open Source Project
4 */
5
Dmitrii Merkurev308252d2023-04-12 19:49:30 +01006#include <fastboot.h>
7#include <net.h>
8#include <net/fastboot_tcp.h>
9#include <net/tcp.h>
10
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +030011#define FASTBOOT_TCP_PORT 5554
12
13static char command[FASTBOOT_COMMAND_LEN];
14static char response[FASTBOOT_RESPONSE_LEN];
Dmitrii Merkurev308252d2023-04-12 19:49:30 +010015
16static const unsigned short handshake_length = 4;
17static const uchar *handshake = "FB01";
18
Dmitrii Merkurev308252d2023-04-12 19:49:30 +010019static u32 curr_tcp_seq_num;
20static u32 curr_tcp_ack_num;
21static unsigned int curr_request_len;
22static enum fastboot_tcp_state {
23 FASTBOOT_CLOSED,
24 FASTBOOT_CONNECTED,
25 FASTBOOT_DISCONNECTING
26} state = FASTBOOT_CLOSED;
27
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +030028static void fastboot_tcp_answer(struct tcp_stream *tcp, u8 action,
29 unsigned int len)
Dmitrii Merkurev308252d2023-04-12 19:49:30 +010030{
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
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +030035 net_send_tcp_packet(len, tcp->rhost, tcp->rport, tcp->lport,
Dmitrii Merkurev308252d2023-04-12 19:49:30 +010036 action, response_seq_num, response_ack_num);
37}
38
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +030039static void fastboot_tcp_reset(struct tcp_stream *tcp)
Dmitrii Merkurev308252d2023-04-12 19:49:30 +010040{
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +030041 fastboot_tcp_answer(tcp, TCP_RST, 0);
Dmitrii Merkurev308252d2023-04-12 19:49:30 +010042 state = FASTBOOT_CLOSED;
43}
44
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +030045static void fastboot_tcp_send_packet(struct tcp_stream *tcp, u8 action,
46 const uchar *data, unsigned int len)
Dmitrii Merkurev308252d2023-04-12 19:49:30 +010047{
48 uchar *pkt = net_get_async_tx_pkt_buf();
49
50 memset(pkt, '\0', PKTSIZE);
51 pkt += net_eth_hdr_size() + IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2;
52 memcpy(pkt, data, len);
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +030053 fastboot_tcp_answer(tcp, action, len);
Dmitrii Merkurev308252d2023-04-12 19:49:30 +010054 memset(pkt, '\0', PKTSIZE);
55}
56
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +030057static void fastboot_tcp_send_message(struct tcp_stream *tcp,
58 const char *message, unsigned int len)
Dmitrii Merkurev308252d2023-04-12 19:49:30 +010059{
60 __be64 len_be = __cpu_to_be64(len);
61 uchar *pkt = net_get_async_tx_pkt_buf();
62
63 memset(pkt, '\0', PKTSIZE);
64 pkt += net_eth_hdr_size() + IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2;
65 // Put first 8 bytes as a big endian message length
66 memcpy(pkt, &len_be, 8);
67 pkt += 8;
68 memcpy(pkt, message, len);
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +030069 fastboot_tcp_answer(tcp, TCP_ACK | TCP_PUSH, len + 8);
Dmitrii Merkurev308252d2023-04-12 19:49:30 +010070 memset(pkt, '\0', PKTSIZE);
71}
72
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +030073static void fastboot_tcp_handler_ipv4(struct tcp_stream *tcp, uchar *pkt,
Dmitrii Merkurev308252d2023-04-12 19:49:30 +010074 u32 tcp_seq_num, u32 tcp_ack_num,
75 u8 action, unsigned int len)
76{
Dmitrii Merkurev07882902023-04-12 19:49:31 +010077 int fastboot_command_id;
Dmitrii Merkurev308252d2023-04-12 19:49:30 +010078 u64 command_size;
79 u8 tcp_fin = action & TCP_FIN;
80 u8 tcp_push = action & TCP_PUSH;
81
Dmitrii Merkurev308252d2023-04-12 19:49:30 +010082 curr_tcp_seq_num = tcp_seq_num;
83 curr_tcp_ack_num = tcp_ack_num;
84 curr_request_len = len;
85
86 switch (state) {
87 case FASTBOOT_CLOSED:
88 if (tcp_push) {
89 if (len != handshake_length ||
90 strlen(pkt) != handshake_length ||
91 memcmp(pkt, handshake, handshake_length) != 0) {
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +030092 fastboot_tcp_reset(tcp);
Dmitrii Merkurev308252d2023-04-12 19:49:30 +010093 break;
94 }
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +030095 fastboot_tcp_send_packet(tcp, TCP_ACK | TCP_PUSH,
Dmitrii Merkurev308252d2023-04-12 19:49:30 +010096 handshake, handshake_length);
97 state = FASTBOOT_CONNECTED;
98 }
99 break;
100 case FASTBOOT_CONNECTED:
101 if (tcp_fin) {
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300102 fastboot_tcp_answer(tcp, TCP_FIN | TCP_ACK, 0);
Dmitrii Merkurev308252d2023-04-12 19:49:30 +0100103 state = FASTBOOT_DISCONNECTING;
104 break;
105 }
106 if (tcp_push) {
107 // First 8 bytes is big endian message length
108 command_size = __be64_to_cpu(*(u64 *)pkt);
109 len -= 8;
110 pkt += 8;
111
112 // Only single packet messages are supported ATM
113 if (strlen(pkt) != command_size) {
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300114 fastboot_tcp_reset(tcp);
Dmitrii Merkurev308252d2023-04-12 19:49:30 +0100115 break;
116 }
117 strlcpy(command, pkt, len + 1);
Dmitrii Merkurev07882902023-04-12 19:49:31 +0100118 fastboot_command_id = fastboot_handle_command(command, response);
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300119 fastboot_tcp_send_message(tcp, response, strlen(response));
Dmitrii Merkurev07882902023-04-12 19:49:31 +0100120 fastboot_handle_boot(fastboot_command_id,
121 strncmp("OKAY", response, 4) == 0);
Dmitrii Merkurev308252d2023-04-12 19:49:30 +0100122 }
123 break;
124 case FASTBOOT_DISCONNECTING:
125 if (tcp_push)
126 state = FASTBOOT_CLOSED;
127 break;
128 }
129
130 memset(command, 0, FASTBOOT_COMMAND_LEN);
131 memset(response, 0, FASTBOOT_RESPONSE_LEN);
Dmitrii Merkurev308252d2023-04-12 19:49:30 +0100132 curr_tcp_seq_num = 0;
133 curr_tcp_ack_num = 0;
134 curr_request_len = 0;
135}
136
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300137static int incoming_filter(struct in_addr rhost, u16 rport, u16 lport)
138{
139 return (lport == FASTBOOT_TCP_PORT);
140}
141
Dmitrii Merkurev308252d2023-04-12 19:49:30 +0100142void fastboot_tcp_start_server(void)
143{
144 printf("Using %s device\n", eth_get_name());
145 printf("Listening for fastboot command on tcp %pI4\n", &net_ip);
146
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300147 tcp_set_incoming_filter(incoming_filter);
Dmitrii Merkurev308252d2023-04-12 19:49:30 +0100148 tcp_set_tcp_handler(fastboot_tcp_handler_ipv4);
149}