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 12static char command[FASTBOOT_COMMAND_LEN] = {0}; 13static char response[FASTBOOT_RESPONSE_LEN] = {0}; 14 15static const unsigned short handshake_length = 4; 16static const uchar *handshake = "FB01"; 17 18static u16 curr_sport; 19static u16 curr_dport; 20static u32 curr_tcp_seq_num; 21static u32 curr_tcp_ack_num; 22static unsigned int curr_request_len; 23static enum fastboot_tcp_state { 24 FASTBOOT_CLOSED, 25 FASTBOOT_CONNECTED, 26 FASTBOOT_DISCONNECTING 27} state = FASTBOOT_CLOSED; 28 29static 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 39static void fastboot_tcp_reset(void) 40{ 41 fastboot_tcp_answer(TCP_RST, 0); 42 state = FASTBOOT_CLOSED; 43} 44 45static 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 56static 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 71static 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 140void 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