| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- // SPDX-License-Identifier: BSD-2-Clause
- /*
- * Copyright (C) 2023 The Android Open Source Project
- */
- #include <common.h>
- #include <fastboot.h>
- #include <net.h>
- #include <net/fastboot_tcp.h>
- #include <net/tcp.h>
- static char command[FASTBOOT_COMMAND_LEN] = {0};
- static char response[FASTBOOT_RESPONSE_LEN] = {0};
- static const unsigned short handshake_length = 4;
- static const uchar *handshake = "FB01";
- static u16 curr_sport;
- static u16 curr_dport;
- static u32 curr_tcp_seq_num;
- static u32 curr_tcp_ack_num;
- static unsigned int curr_request_len;
- static enum fastboot_tcp_state {
- FASTBOOT_CLOSED,
- FASTBOOT_CONNECTED,
- FASTBOOT_DISCONNECTING
- } state = FASTBOOT_CLOSED;
- static void fastboot_tcp_answer(u8 action, unsigned int len)
- {
- const u32 response_seq_num = curr_tcp_ack_num;
- const u32 response_ack_num = curr_tcp_seq_num +
- (curr_request_len > 0 ? curr_request_len : 1);
- net_send_tcp_packet(len, htons(curr_sport), htons(curr_dport),
- action, response_seq_num, response_ack_num);
- }
- static void fastboot_tcp_reset(void)
- {
- fastboot_tcp_answer(TCP_RST, 0);
- state = FASTBOOT_CLOSED;
- }
- static void fastboot_tcp_send_packet(u8 action, const uchar *data, unsigned int len)
- {
- uchar *pkt = net_get_async_tx_pkt_buf();
- memset(pkt, '\0', PKTSIZE);
- pkt += net_eth_hdr_size() + IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2;
- memcpy(pkt, data, len);
- fastboot_tcp_answer(action, len);
- memset(pkt, '\0', PKTSIZE);
- }
- static void fastboot_tcp_send_message(const char *message, unsigned int len)
- {
- __be64 len_be = __cpu_to_be64(len);
- uchar *pkt = net_get_async_tx_pkt_buf();
- memset(pkt, '\0', PKTSIZE);
- pkt += net_eth_hdr_size() + IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2;
- // Put first 8 bytes as a big endian message length
- memcpy(pkt, &len_be, 8);
- pkt += 8;
- memcpy(pkt, message, len);
- fastboot_tcp_answer(TCP_ACK | TCP_PUSH, len + 8);
- memset(pkt, '\0', PKTSIZE);
- }
- static void 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)
- {
- int fastboot_command_id;
- u64 command_size;
- u8 tcp_fin = action & TCP_FIN;
- u8 tcp_push = action & TCP_PUSH;
- curr_sport = sport;
- curr_dport = dport;
- curr_tcp_seq_num = tcp_seq_num;
- curr_tcp_ack_num = tcp_ack_num;
- curr_request_len = len;
- switch (state) {
- case FASTBOOT_CLOSED:
- if (tcp_push) {
- if (len != handshake_length ||
- strlen(pkt) != handshake_length ||
- memcmp(pkt, handshake, handshake_length) != 0) {
- fastboot_tcp_reset();
- break;
- }
- fastboot_tcp_send_packet(TCP_ACK | TCP_PUSH,
- handshake, handshake_length);
- state = FASTBOOT_CONNECTED;
- }
- break;
- case FASTBOOT_CONNECTED:
- if (tcp_fin) {
- fastboot_tcp_answer(TCP_FIN | TCP_ACK, 0);
- state = FASTBOOT_DISCONNECTING;
- break;
- }
- if (tcp_push) {
- // First 8 bytes is big endian message length
- command_size = __be64_to_cpu(*(u64 *)pkt);
- len -= 8;
- pkt += 8;
- // Only single packet messages are supported ATM
- if (strlen(pkt) != command_size) {
- fastboot_tcp_reset();
- break;
- }
- strlcpy(command, pkt, len + 1);
- fastboot_command_id = fastboot_handle_command(command, response);
- fastboot_tcp_send_message(response, strlen(response));
- fastboot_handle_boot(fastboot_command_id,
- strncmp("OKAY", response, 4) == 0);
- }
- break;
- case FASTBOOT_DISCONNECTING:
- if (tcp_push)
- state = FASTBOOT_CLOSED;
- break;
- }
- memset(command, 0, FASTBOOT_COMMAND_LEN);
- memset(response, 0, FASTBOOT_RESPONSE_LEN);
- curr_sport = 0;
- curr_dport = 0;
- curr_tcp_seq_num = 0;
- curr_tcp_ack_num = 0;
- curr_request_len = 0;
- }
- void fastboot_tcp_start_server(void)
- {
- printf("Using %s device\n", eth_get_name());
- printf("Listening for fastboot command on tcp %pI4\n", &net_ip);
- tcp_set_tcp_handler(fastboot_tcp_handler_ipv4);
- }
|