Browse Source

net: add fastboot TCP support

Known limitations are
1. fastboot reboot doesn't work (answering OK but not rebooting)
2. flashing isn't supported (TCP transport only limitation)

The command syntax is
fastboot tcp

Signed-off-by: Dmitrii Merkurev <dimorinny@google.com>
Cc: Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
Cc: Simon Glass <sjg@chromium.org>
Сс: Joe Hershberger <joe.hershberger@ni.com>
Сс: Ramon Fried <rfried.dev@gmail.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Dmitrii Merkurev 1 year ago
parent
commit
443d319180

+ 4 - 2
MAINTAINERS

@@ -988,10 +988,12 @@ F:	cmd/fastboot.c
 F:	doc/android/fastboot*.rst
 F:	include/fastboot.h
 F:	include/fastboot-internal.h
-F:	include/net/fastboot.h
+F:	include/net/fastboot_tcp.h
+F:	include/net/fastboot_udp.h
 F:	drivers/fastboot/
 F:	drivers/usb/gadget/f_fastboot.c
-F:	net/fastboot.c
+F:	net/fastboot_tcp.c
+F:	net/fastboot_udp.c
 F:	test/dm/fastboot.c
 
 FPGA

+ 23 - 2
cmd/fastboot.c

@@ -26,7 +26,7 @@ static int do_fastboot_udp(int argc, char *const argv[],
 		return CMD_RET_FAILURE;
 	}
 
-	err = net_loop(FASTBOOT);
+	err = net_loop(FASTBOOT_UDP);
 
 	if (err < 0) {
 		printf("fastboot udp error: %d\n", err);
@@ -36,6 +36,26 @@ static int do_fastboot_udp(int argc, char *const argv[],
 	return CMD_RET_SUCCESS;
 }
 
+static int do_fastboot_tcp(int argc, char *const argv[],
+			   uintptr_t buf_addr, size_t buf_size)
+{
+	int err;
+
+	if (!IS_ENABLED(CONFIG_TCP_FUNCTION_FASTBOOT)) {
+		pr_err("Fastboot TCP not enabled\n");
+		return CMD_RET_FAILURE;
+	}
+
+	err = net_loop(FASTBOOT_TCP);
+
+	if (err < 0) {
+		printf("fastboot tcp error: %d\n", err);
+		return CMD_RET_FAILURE;
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
 static int do_fastboot_usb(int argc, char *const argv[],
 			   uintptr_t buf_addr, size_t buf_size)
 {
@@ -141,7 +161,8 @@ NXTARG:
 
 	if (!strcmp(argv[1], "udp"))
 		return do_fastboot_udp(argc, argv, buf_addr, buf_size);
-
+	if (!strcmp(argv[1], "tcp"))
+		return do_fastboot_tcp(argc, argv, buf_addr, buf_size);
 	if (!strcmp(argv[1], "usb")) {
 		argv++;
 		argc--;

+ 14 - 0
drivers/fastboot/Kconfig

@@ -4,6 +4,13 @@ config FASTBOOT
 	bool
 	imply ANDROID_BOOT_IMAGE
 	imply CMD_FASTBOOT
+	help
+	  Fastboot is a protocol used in Android devices for
+	  communicating between the device and a computer during
+	  the bootloader stage. It allows the user to flash the
+	  device firmware and unlock the bootloader.
+	  More information about the protocol and usecases:
+	  https://android.googlesource.com/platform/system/core/+/refs/heads/master/fastboot/
 
 config USB_FUNCTION_FASTBOOT
 	bool "Enable USB fastboot gadget"
@@ -28,6 +35,13 @@ config UDP_FUNCTION_FASTBOOT_PORT
 	help
 	  The fastboot protocol requires a UDP port number.
 
+config TCP_FUNCTION_FASTBOOT
+	depends on NET
+	select FASTBOOT
+	bool "Enable fastboot protocol over TCP"
+	help
+	  This enables the fastboot protocol over TCP.
+
 if FASTBOOT
 
 config FASTBOOT_BUF_ADDR

+ 0 - 1
drivers/fastboot/fb_common.c

@@ -15,7 +15,6 @@
 #include <command.h>
 #include <env.h>
 #include <fastboot.h>
-#include <net/fastboot.h>
 
 /**
  * fastboot_buf_addr - base address of the fastboot download buffer

+ 2 - 1
include/net.h

@@ -507,7 +507,8 @@ extern int		net_restart_wrap;	/* Tried all network devices */
 
 enum proto_t {
 	BOOTP, RARP, ARP, TFTPGET, DHCP, DHCP6, PING, PING6, DNS, NFS, CDP,
-	NETCONS, SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI, WGET
+	NETCONS, SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT_UDP, FASTBOOT_TCP,
+	WOL, UDP, NCSI, WGET
 };
 
 extern char	net_boot_file_name[1024];/* Boot File name */

+ 0 - 21
include/net/fastboot.h

@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: BSD-2-Clause
- *
- * Copyright (C) 2016 The Android Open Source Project
- */
-
-#ifndef __NET_FASTBOOT_H__
-#define __NET_FASTBOOT_H__
-
-/**********************************************************************/
-/*
- *	Global functions and variables.
- */
-
-/**
- * Wait for incoming fastboot comands.
- */
-void fastboot_start_server(void);
-
-/**********************************************************************/
-
-#endif /* __NET_FASTBOOT_H__ */

+ 14 - 0
include/net/fastboot_tcp.h

@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2023 The Android Open Source Project
+ */
+
+#ifndef __NET_FASTBOOT_TCP_H__
+#define __NET_FASTBOOT_TCP_H__
+
+/**
+ * Wait for incoming tcp fastboot comands.
+ */
+void fastboot_tcp_start_server(void);
+
+#endif /* __NET_FASTBOOT_TCP_H__ */

+ 14 - 0
include/net/fastboot_udp.h

@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2016 The Android Open Source Project
+ */
+
+#ifndef __NET_FASTBOOT_H__
+#define __NET_FASTBOOT_H__
+
+/**
+ * Wait for incoming UDP fastboot comands.
+ */
+void fastboot_udp_start_server(void);
+
+#endif /* __NET_FASTBOOT_H__ */

+ 2 - 1
net/Makefile

@@ -27,7 +27,8 @@ obj-$(CONFIG_CMD_PCAP) += pcap.o
 obj-$(CONFIG_CMD_RARP) += rarp.o
 obj-$(CONFIG_CMD_SNTP) += sntp.o
 obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o
-obj-$(CONFIG_UDP_FUNCTION_FASTBOOT)  += fastboot.o
+obj-$(CONFIG_UDP_FUNCTION_FASTBOOT)  += fastboot_udp.o
+obj-$(CONFIG_TCP_FUNCTION_FASTBOOT)  += fastboot_tcp.o
 obj-$(CONFIG_CMD_WOL)  += wol.o
 obj-$(CONFIG_PROT_UDP) += udp.o
 obj-$(CONFIG_PROT_TCP) += tcp.o

+ 143 - 0
net/fastboot_tcp.c

@@ -0,0 +1,143 @@
+// 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)
+{
+	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_handle_command(command, response);
+			fastboot_tcp_send_message(response, strlen(response));
+		}
+		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);
+}

+ 2 - 2
net/fastboot.c → net/fastboot_udp.c

@@ -7,7 +7,7 @@
 #include <command.h>
 #include <fastboot.h>
 #include <net.h>
-#include <net/fastboot.h>
+#include <net/fastboot_udp.h>
 
 enum {
 	FASTBOOT_ERROR = 0,
@@ -300,7 +300,7 @@ static void fastboot_handler(uchar *packet, unsigned int dport,
 	}
 }
 
-void fastboot_start_server(void)
+void fastboot_udp_start_server(void)
 {
 	printf("Using %s device\n", eth_get_name());
 	printf("Listening for fastboot command on %pI4\n", &net_ip);

+ 12 - 5
net/net.c

@@ -93,7 +93,8 @@
 #include <net.h>
 #include <net6.h>
 #include <ndisc.h>
-#include <net/fastboot.h>
+#include <net/fastboot_udp.h>
+#include <net/fastboot_tcp.h>
 #include <net/tftp.h>
 #include <net/ncsi.h>
 #if defined(CONFIG_CMD_PCAP)
@@ -501,9 +502,14 @@ restart:
 			tftp_start_server();
 			break;
 #endif
-#ifdef CONFIG_UDP_FUNCTION_FASTBOOT
-		case FASTBOOT:
-			fastboot_start_server();
+#if defined(CONFIG_UDP_FUNCTION_FASTBOOT)
+		case FASTBOOT_UDP:
+			fastboot_udp_start_server();
+			break;
+#endif
+#if defined(CONFIG_TCP_FUNCTION_FASTBOOT)
+		case FASTBOOT_TCP:
+			fastboot_tcp_start_server();
 			break;
 #endif
 #if defined(CONFIG_CMD_DHCP)
@@ -1498,7 +1504,8 @@ common:
 		/* Fall through */
 
 	case NETCONS:
-	case FASTBOOT:
+	case FASTBOOT_UDP:
+	case FASTBOOT_TCP:
 	case TFTPSRV:
 		if (IS_ENABLED(CONFIG_IPV6) && use_ip6) {
 			if (!memcmp(&net_link_local_ip6, &net_null_addr_ip6,