Adds test cases 200x for TCP/UDP iSocket interface

Acked-by: Etienne Carriere <etienne.carriere@linaro.org>
Acked-by: Jerome Forissier <jerome.forissier@linaro.org>
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
diff --git a/host/xtest/xtest_2000.c b/host/xtest/xtest_2000.c
new file mode 100644
index 0000000..3cba3ee
--- /dev/null
+++ b/host/xtest/xtest_2000.c
@@ -0,0 +1,908 @@
+/*
+ * Copyright (c) 2016, Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <assert.h>
+#include <err.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <ta_socket.h>
+#include <tee_isocket.h>
+#include <tee_tcpsocket.h>
+#include <__tee_tcpsocket_defines_extensions.h>
+#include <tee_udpsocket.h>
+#include <unistd.h>
+
+#include "xtest_test.h"
+#include "xtest_helpers.h"
+#include "sock_server.h"
+#include "rand_stream.h"
+
+struct socket_handle {
+	uint64_t buf[2];
+	size_t blen;
+};
+
+static TEE_Result socket_tcp_open(TEEC_Session *session, uint32_t ip_vers,
+				  const char *addr, uint16_t port,
+				  struct socket_handle *handle,
+				  uint32_t *error, uint32_t *ret_orig)
+{
+	TEE_Result res;
+	TEEC_Operation op = TEEC_OPERATION_INITIALIZER;
+
+	memset(handle, 0, sizeof(*handle));
+
+	op.params[0].value.a = ip_vers;
+	op.params[0].value.b = port;
+	op.params[1].tmpref.buffer = (void *)addr;
+	op.params[1].tmpref.size = strlen(addr) + 1;
+	op.params[2].tmpref.buffer = handle->buf;
+	op.params[2].tmpref.size = sizeof(handle->buf);
+
+	op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT,
+					 TEEC_MEMREF_TEMP_INPUT,
+					 TEEC_MEMREF_TEMP_OUTPUT,
+					 TEEC_VALUE_OUTPUT);
+
+	res = TEEC_InvokeCommand(session, TA_SOCKET_CMD_TCP_OPEN,
+				 &op, ret_orig);
+
+	handle->blen = op.params[2].tmpref.size;
+	*error = op.params[3].value.a;
+	return res;
+}
+
+static TEE_Result socket_udp_open(TEEC_Session *session, uint32_t ip_vers,
+				  const char *addr, uint16_t port,
+				  struct socket_handle *handle,
+				  uint32_t *error, uint32_t *ret_orig)
+{
+	TEE_Result res;
+	TEEC_Operation op = TEEC_OPERATION_INITIALIZER;
+
+	memset(handle, 0, sizeof(*handle));
+
+	op.params[0].value.a = ip_vers;
+	op.params[0].value.b = port;
+	op.params[1].tmpref.buffer = (void *)addr;
+	op.params[1].tmpref.size = strlen(addr) + 1;
+	op.params[2].tmpref.buffer = handle->buf;
+	op.params[2].tmpref.size = sizeof(handle->buf);
+
+	op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT,
+					 TEEC_MEMREF_TEMP_INPUT,
+					 TEEC_MEMREF_TEMP_OUTPUT,
+					 TEEC_VALUE_OUTPUT);
+
+	res = TEEC_InvokeCommand(session, TA_SOCKET_CMD_UDP_OPEN,
+				 &op, ret_orig);
+
+	handle->blen = op.params[2].tmpref.size;
+	*error = op.params[3].value.a;
+	return res;
+}
+
+static TEE_Result socket_send(TEEC_Session *session,
+			      struct socket_handle *handle,
+			      const void *data, size_t *dlen,
+			      uint32_t timeout, uint32_t *ret_orig)
+{
+	TEE_Result res;
+	TEEC_Operation op = TEEC_OPERATION_INITIALIZER;
+
+	op.params[0].tmpref.buffer = handle->buf;
+	op.params[0].tmpref.size = handle->blen;
+	op.params[1].tmpref.buffer = (void *)data;
+	op.params[1].tmpref.size = *dlen;
+	op.params[2].value.a = timeout;
+
+	op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,
+					 TEEC_MEMREF_TEMP_INPUT,
+					 TEEC_VALUE_INOUT, TEEC_NONE);
+
+	res = TEEC_InvokeCommand(session, TA_SOCKET_CMD_SEND, &op, ret_orig);
+
+	*dlen = op.params[2].value.b;
+	return res;
+}
+
+static TEE_Result socket_recv(TEEC_Session *session,
+			      struct socket_handle *handle,
+			      void *data, size_t *dlen,
+			      uint32_t timeout, uint32_t *ret_orig)
+{
+	TEE_Result res;
+	TEEC_Operation op = TEEC_OPERATION_INITIALIZER;
+
+	op.params[0].tmpref.buffer = handle->buf;
+	op.params[0].tmpref.size = handle->blen;
+	op.params[1].tmpref.buffer = (void *)data;
+	op.params[1].tmpref.size = *dlen;
+	op.params[2].value.a = timeout;
+
+	op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,
+					 TEEC_MEMREF_TEMP_OUTPUT,
+					 TEEC_VALUE_INPUT, TEEC_NONE);
+
+	res = TEEC_InvokeCommand(session, TA_SOCKET_CMD_RECV, &op, ret_orig);
+
+	*dlen = op.params[1].tmpref.size;
+	return res;
+}
+
+static TEE_Result socket_get_error(TEEC_Session *session,
+			      struct socket_handle *handle,
+			      uint32_t *proto_error, uint32_t *ret_orig)
+{
+	TEE_Result res;
+	TEEC_Operation op = TEEC_OPERATION_INITIALIZER;
+
+	op.params[0].tmpref.buffer = handle->buf;
+	op.params[0].tmpref.size = handle->blen;
+
+	op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,
+					 TEEC_VALUE_OUTPUT,
+					 TEEC_NONE, TEEC_NONE);
+
+	res = TEEC_InvokeCommand(session, TA_SOCKET_CMD_ERROR, &op, ret_orig);
+
+	*proto_error = op.params[1].value.a;
+	return res;
+}
+
+static TEE_Result socket_close(TEEC_Session *session,
+			      struct socket_handle *handle, uint32_t *ret_orig)
+{
+	TEEC_Operation op = TEEC_OPERATION_INITIALIZER;
+
+	op.params[0].tmpref.buffer = handle->buf;
+	op.params[0].tmpref.size = handle->blen;
+
+	op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,
+					 TEEC_NONE, TEEC_NONE, TEEC_NONE);
+
+	return TEEC_InvokeCommand(session, TA_SOCKET_CMD_CLOSE, &op, ret_orig);
+}
+
+static TEE_Result socket_ioctl(TEEC_Session *session,
+			      struct socket_handle *handle, uint32_t ioctl_cmd,
+			      void *data, size_t *dlen, uint32_t *ret_orig)
+{
+	TEE_Result res;
+	TEEC_Operation op = TEEC_OPERATION_INITIALIZER;
+
+	op.params[0].tmpref.buffer = handle->buf;
+	op.params[0].tmpref.size = handle->blen;
+	op.params[1].tmpref.buffer = data;
+	op.params[1].tmpref.size = *dlen;
+	op.params[2].value.a = ioctl_cmd;
+
+	op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,
+					 TEEC_MEMREF_TEMP_INOUT,
+					 TEEC_VALUE_INPUT, TEEC_NONE);
+
+	res = TEEC_InvokeCommand(session, TA_SOCKET_CMD_IOCTL, &op, ret_orig);
+
+	*dlen = op.params[1].tmpref.size;
+	return res;
+}
+
+
+
+struct test_200x_io_state {
+	struct rand_stream *read_rs;
+	struct rand_stream *write_rs;
+	bool rfail;
+};
+
+static void test_200x_init_io_state(struct test_200x_io_state *s,
+				    int read_seed, int write_seed)
+{
+	memset(s, 0, sizeof(*s));
+	s->read_rs = rand_stream_alloc(read_seed, 100);
+	s->write_rs = rand_stream_alloc(write_seed, 100);
+	assert(s->read_rs && s->write_rs);
+}
+
+static bool test_200x_tcp_accept_cb(void *ptr, int fd, short *events)
+{
+	(void)ptr;
+	(void)fd;
+	(void)events;
+	return true;
+}
+
+static bool test_200x_tcp_read_cb(void *ptr, int fd, short *events)
+{
+	struct test_200x_io_state *iostate = ptr;
+	ssize_t r;
+	uint8_t buf[100];
+	uint8_t buf2[100];
+
+	(void)events;
+	r = read(fd, buf, sizeof(buf));
+	if (r <= 0)
+		return false;
+
+	rand_stream_read(iostate->read_rs, buf2, r);
+	if (memcmp(buf, buf2, r)) {
+		iostate->rfail = true;
+		return false;
+	}
+
+	return true;
+}
+
+static bool test_200x_tcp_write_cb(void *ptr, int fd, short *events)
+{
+	struct test_200x_io_state *iostate = ptr;
+	size_t num_bytes = 100;
+	const void *bytes;
+	ssize_t r;
+
+	(void)events;
+
+	bytes = rand_stream_peek(iostate->write_rs, &num_bytes);
+	r = write(fd, bytes, num_bytes);
+	if (r < 0)
+		return false;
+
+	rand_stream_advance(iostate->write_rs, num_bytes);
+	return true;
+}
+
+static void xtest_tee_test_2001(ADBG_Case_t *c)
+{
+	struct sock_server ts;
+	TEEC_Session session = { 0 };
+	uint32_t ret_orig;
+	uint32_t proto_error;
+	struct socket_handle sh;
+	uint8_t buf[64];
+	uint8_t buf2[64];
+	size_t blen;
+	struct test_200x_io_state server_iostate;
+	struct test_200x_io_state local_iostate;
+	struct sock_io_cb cb = {
+		.accept = test_200x_tcp_accept_cb,
+		.read = test_200x_tcp_read_cb,
+		.write = test_200x_tcp_write_cb,
+		.ptr = &server_iostate,
+	};
+
+	test_200x_init_io_state(&server_iostate, 1, 2);
+	test_200x_init_io_state(&local_iostate, 2, 1);
+
+	Do_ADBG_BeginSubCase(c, "Start server");
+	if (!ADBG_EXPECT_TRUE(c, sock_server_init_tcp(&ts, &cb)))
+		return;
+	Do_ADBG_EndSubCase(c, "Start server");
+
+	Do_ADBG_BeginSubCase(c, "TCP Socket open");
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, xtest_teec_open_session(
+			&session, &socket_ta_uuid, NULL, &ret_orig)))
+		goto out;
+
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_tcp_open(&session,
+			TEE_IP_VERSION_DC, ts.bind->host, ts.bind->port,
+			&sh, &proto_error, &ret_orig)))
+		goto out_close_session;
+
+	Do_ADBG_EndSubCase(c, "TCP Socket open");
+
+	Do_ADBG_BeginSubCase(c, "TCP Socket send");
+	blen = sizeof(buf);
+	rand_stream_read(local_iostate.write_rs, buf, blen);
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_send(&session, &sh,
+			buf, &blen, TEE_TIMEOUT_INFINITE, &ret_orig)))
+		goto out_close_session;
+	ADBG_EXPECT_COMPARE_UNSIGNED(c, blen, ==, sizeof(buf));
+	Do_ADBG_EndSubCase(c, "TCP Socket send");
+
+	Do_ADBG_BeginSubCase(c, "TCP Socket recv");
+	blen = sizeof(buf);
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_recv(&session, &sh,
+			buf, &blen, TEE_TIMEOUT_INFINITE, &ret_orig)))
+		goto out_close_session;
+	ADBG_EXPECT_COMPARE_UNSIGNED(c, blen, ==, sizeof(buf));
+	rand_stream_read(local_iostate.read_rs, buf2, blen);
+	ADBG_EXPECT_BUFFER(c, buf2, blen, buf, blen);
+	Do_ADBG_EndSubCase(c, "TCP Socket recv");
+
+	/*
+	 * All written bytes above (with the TA) is quite likely to have
+	 * hit the tcp server by now.
+	 */
+	ADBG_EXPECT_TRUE(c, !server_iostate.rfail);
+
+	Do_ADBG_BeginSubCase(c, "TCP Socket get error");
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_get_error(&session, &sh,
+			&proto_error, &ret_orig)))
+		goto out_close_session;
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, proto_error))
+		goto out_close_session;
+	Do_ADBG_EndSubCase(c, "TCP Socket get error");
+
+	Do_ADBG_BeginSubCase(c, "TCP Socket close");
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_close(&session, &sh,
+			&ret_orig)))
+		goto out_close_session;
+	Do_ADBG_EndSubCase(c, "TCP Socket close");
+
+	Do_ADBG_BeginSubCase(c, "TCP Socket ioctl");
+
+
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_tcp_open(&session,
+			TEE_IP_VERSION_DC, ts.bind->host, ts.bind->port,
+			&sh, &proto_error, &ret_orig)))
+		goto out_close_session;
+
+	blen = sizeof(buf);
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_ioctl(&session, &sh, 0x00F00000,
+			buf, &blen, &ret_orig)))
+		goto out_close_session;
+
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_close(&session, &sh,
+			&ret_orig)))
+		goto out_close_session;
+
+
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_tcp_open(&session,
+			TEE_IP_VERSION_DC, ts.bind->host, ts.bind->port,
+			&sh, &proto_error, &ret_orig)))
+		goto out_close_session;
+
+	blen = sizeof(buf);
+	ADBG_EXPECT_TEEC_RESULT(c, TEEC_ERROR_TARGET_DEAD,
+				socket_ioctl(&session, &sh,
+					TEE_ISOCKET_PROTOCOLID_TCP << 24,
+					buf, &blen, &ret_orig));
+	TEEC_CloseSession(&session);
+
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, xtest_teec_open_session(
+			&session, &socket_ta_uuid, NULL, &ret_orig)))
+		goto out;
+
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_tcp_open(&session,
+			TEE_IP_VERSION_DC, ts.bind->host, ts.bind->port,
+			&sh, &proto_error, &ret_orig)))
+		goto out_close_session;
+
+	blen = sizeof(buf);
+	ADBG_EXPECT_TEEC_RESULT(c, TEEC_ERROR_TARGET_DEAD,
+				socket_ioctl(&session, &sh, 0x32 << 24,
+				buf, &blen, &ret_orig));
+
+	Do_ADBG_EndSubCase(c, "TCP Socket ioctl");
+
+out_close_session:
+	TEEC_CloseSession(&session);
+out:
+	sock_server_uninit(&ts);
+}
+
+struct test_2002_barrier {
+	pthread_mutex_t mu;
+	pthread_barrier_t bar;
+};
+
+struct test_2002_arg {
+	bool success;
+	size_t tnum;
+	struct test_2002_barrier *bar;
+};
+
+static void  xtest_2002_wait_barrier(struct test_2002_barrier *bar)
+{
+	/*
+	 * Once the mutex is taken the barrier is initialized so the mutex
+	 * can be released immediately.
+	 */
+	xtest_mutex_lock(&bar->mu);
+	xtest_mutex_unlock(&bar->mu);
+	xtest_barrier_wait(&bar->bar);
+}
+
+static void *xtest_tee_test_2002_thread(void *arg)
+{
+	struct test_2002_arg *a = arg;
+	TEE_Result res;
+	struct sock_server ts;
+	TEEC_Session session = { 0 };
+	uint32_t ret_orig;
+	uint32_t proto_error;
+	struct socket_handle sh;
+	struct test_200x_io_state server_iostate;
+	struct test_200x_io_state local_iostate;
+	struct sock_io_cb cb = {
+		.accept = test_200x_tcp_accept_cb,
+		.read = test_200x_tcp_read_cb,
+		.write = test_200x_tcp_write_cb,
+		.ptr = &server_iostate,
+	};
+	int seed[2] = { 1 + a->tnum * 2, 2 + a->tnum * 2 };
+	size_t send_limit = 10000;
+	size_t recv_limit = 10000;
+	size_t sent_bytes = 0;
+	size_t recvd_bytes = 0;
+
+	test_200x_init_io_state(&server_iostate, seed[0], seed[1]);
+	test_200x_init_io_state(&local_iostate, seed[1], seed[0]);
+
+	if (!sock_server_init_tcp(&ts, &cb)) {
+		xtest_2002_wait_barrier(a->bar);
+		return NULL;
+	}
+
+	res = xtest_teec_open_session(&session, &socket_ta_uuid, NULL,
+				      &ret_orig);
+
+	xtest_2002_wait_barrier(a->bar);
+	if (res != TEE_SUCCESS)
+		goto out;
+
+	res = socket_tcp_open(&session, TEE_IP_VERSION_DC, ts.bind->host,
+			      ts.bind->port, &sh, &proto_error, &ret_orig);
+	if (res != TEE_SUCCESS)
+		goto out_close_session;
+
+	while (sent_bytes < send_limit && recvd_bytes < recv_limit) {
+		const void *peek;
+		uint8_t buf[64];
+		uint8_t buf2[64];
+		size_t blen;
+
+		blen = sizeof(buf);
+		peek = rand_stream_peek(local_iostate.write_rs, &blen);
+		res = socket_send(&session, &sh, peek, &blen,
+				  TEE_TIMEOUT_INFINITE, &ret_orig);
+		if (res != TEE_SUCCESS)
+			goto out_close_session;
+		rand_stream_advance(local_iostate.write_rs, blen);
+		sent_bytes += blen;
+
+		blen = sizeof(buf);
+		res = socket_recv(&session, &sh, buf, &blen,
+				  TEE_TIMEOUT_INFINITE, &ret_orig);
+		if (res != TEE_SUCCESS)
+			goto out_close_session;
+		rand_stream_read(local_iostate.read_rs, buf2, blen);
+		if (memcmp(buf2, buf, blen))
+			goto out_close_session;
+		recvd_bytes += blen;
+	}
+
+
+	res = socket_close(&session, &sh, &ret_orig);
+	if (res != TEE_SUCCESS)
+		goto out_close_session;
+
+	/*
+	 * All written bytes above (with the TA) is quite likely to have
+	 * hit the tcp server by now.
+	 */
+	a->success = !server_iostate.rfail;
+
+out_close_session:
+	TEEC_CloseSession(&session);
+out:
+	sock_server_uninit(&ts);
+	return NULL;
+}
+
+#define NUM_THREADS	3
+
+static void xtest_tee_test_2002(ADBG_Case_t *c)
+{
+	pthread_t thr[NUM_THREADS];
+	struct test_2002_barrier bar = { .mu = PTHREAD_MUTEX_INITIALIZER };
+	struct test_2002_arg arg[NUM_THREADS];
+	size_t n;
+	size_t nt;
+
+	Do_ADBG_BeginSubCase(c, "Stressing with %d threads", NUM_THREADS);
+
+	xtest_mutex_lock(&bar.mu);
+
+	nt = NUM_THREADS;
+	for (n = 0; n < nt; n++) {
+		arg[n].success = false;
+		arg[n].tnum = n;
+		arg[n].bar = &bar;
+		if (!ADBG_EXPECT(c, 0, pthread_create(thr + n, NULL,
+					xtest_tee_test_2002_thread, arg + n)))
+			nt = n; /* break loop and start cleanup */
+	}
+
+	xtest_barrier_init(&bar.bar, nt + 1);
+	xtest_mutex_unlock(&bar.mu);
+	xtest_barrier_wait(&bar.bar);
+
+	for (n = 0; n < nt; n++) {
+		ADBG_EXPECT(c, 0, pthread_join(thr[n], NULL));
+		ADBG_EXPECT_TRUE(c, arg[n].success);
+	}
+
+	xtest_mutex_destroy(&bar.mu);
+	xtest_barrier_destroy(&bar.bar);
+
+	Do_ADBG_EndSubCase(c, "Stressing with %d threads", NUM_THREADS);
+}
+
+static bool test_2003_accept_cb(void *ptr, int fd, short *events)
+{
+	int val;
+
+	(void)ptr;
+	(void)events;
+
+	val = 4 * 1024;
+	if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)))
+		warn("test_2003_accept_cb: setsockopt");
+	return true;
+}
+
+static void xtest_tee_test_2003(ADBG_Case_t *c)
+{
+	struct sock_server ts;
+	TEEC_Session session = { 0 };
+	uint32_t ret_orig;
+	uint32_t proto_error;
+	struct socket_handle sh;
+	void *buf;
+	const size_t blen0 = 16 * 1024;
+	size_t blen;
+	uint32_t val;
+	struct sock_io_cb cb = { .accept = test_2003_accept_cb };
+
+	Do_ADBG_BeginSubCase(c, "Start server");
+	if (!ADBG_EXPECT_TRUE(c, sock_server_init_tcp(&ts, &cb)))
+		return;
+	buf = calloc(1, blen0);
+	if (!ADBG_EXPECT_NOT_NULL(c, buf))
+		goto out;
+	Do_ADBG_EndSubCase(c, "Start server");
+
+	Do_ADBG_BeginSubCase(c, "TCP Socket open");
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, xtest_teec_open_session(
+			&session, &socket_ta_uuid, NULL, &ret_orig)))
+		goto out;
+
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_tcp_open(&session,
+			TEE_IP_VERSION_DC, ts.bind->host, ts.bind->port,
+			&sh, &proto_error, &ret_orig)))
+		goto out_close_session;
+
+	blen = sizeof(val);
+	val = 4 * 1024;
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_ioctl(&session, &sh,
+			TEE_TCP_SET_RECVBUF, &val, &blen, &ret_orig)))
+		goto out_close_session;
+
+	blen = sizeof(val);
+	val = 4 * 1024;
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_ioctl(&session, &sh,
+			TEE_TCP_SET_SENDBUF, &val, &blen, &ret_orig)))
+		goto out_close_session;
+
+	Do_ADBG_EndSubCase(c, "TCP Socket open");
+
+	Do_ADBG_BeginSubCase(c, "TCP Socket send (10 ms timeout)");
+	while (true) {
+		TEE_Result res;
+
+		blen = blen0;
+		memset(buf, 0, blen0);
+		res = socket_send(&session, &sh, buf, &blen, 10, &ret_orig);
+		if (res == TEE_ISOCKET_ERROR_TIMEOUT)
+			break;
+		if (!ADBG_EXPECT_TEEC_SUCCESS(c, res))
+			goto out_close_session;
+		ADBG_EXPECT_COMPARE_UNSIGNED(c, blen, ==, blen0);
+	}
+	Do_ADBG_EndSubCase(c, "TCP Socket send (10 ms timeout)");
+
+	Do_ADBG_BeginSubCase(c, "TCP Socket recv (10 ms timeout)");
+	blen = blen0;
+	ADBG_EXPECT_TEEC_RESULT(c, TEE_ISOCKET_ERROR_TIMEOUT,
+				socket_recv(&session, &sh, buf, &blen,
+					    10, &ret_orig));
+	ADBG_EXPECT_COMPARE_UNSIGNED(c, blen, ==, blen0);
+	Do_ADBG_EndSubCase(c, "TCP Socket recv (10 ms timeout)");
+
+	Do_ADBG_BeginSubCase(c, "TCP Socket get error");
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_get_error(&session, &sh,
+			&proto_error, &ret_orig)))
+		goto out_close_session;
+	ADBG_EXPECT_TEEC_RESULT(c, TEE_ISOCKET_ERROR_TIMEOUT, proto_error);
+	Do_ADBG_EndSubCase(c, "TCP Socket get error");
+
+out_close_session:
+	TEEC_CloseSession(&session);
+out:
+	free(buf);
+	sock_server_uninit(&ts);
+}
+
+static bool test_200x_udp_accept_cb(void *ptr, int fd, short *events)
+{
+	struct test_200x_io_state *iostate = ptr;
+	struct sockaddr_storage sass;
+	struct sockaddr *sa = (struct sockaddr *)&sass;
+	socklen_t slen = sizeof(sass);
+	uint8_t buf[100];
+	uint8_t buf2[100];
+	ssize_t r;
+	size_t l;
+
+	(void)events;
+
+	r = recvfrom(fd, buf, sizeof(buf), 0, sa, &slen);
+	if (r == -1)
+		return false;
+
+	l = r;
+	rand_stream_read(iostate->read_rs, buf2, l);
+	if (memcmp(buf, buf2, l))
+		iostate->rfail = true;
+
+	rand_stream_read(iostate->write_rs, buf, l);
+	return sendto(fd, buf, l, 0, sa, slen) != -1;
+}
+
+static void xtest_tee_test_2004(ADBG_Case_t *c)
+{
+	struct sock_server ts;
+	struct sock_server ts2;
+	struct sock_server ts3;
+	bool ts_inited = false;
+	bool ts2_inited = false;
+	bool ts3_inited = false;
+	TEEC_Session session = { 0 };
+	uint32_t ret_orig;
+	uint32_t proto_error;
+	struct socket_handle sh;
+	uint8_t buf[64];
+	uint8_t buf2[64];
+	size_t blen;
+	uint16_t port;
+	struct test_200x_io_state server_iostate;
+	struct test_200x_io_state local_iostate;
+	struct sock_io_cb cb = {
+		.accept = test_200x_udp_accept_cb,
+		.ptr = &server_iostate,
+	};
+
+	test_200x_init_io_state(&server_iostate, 1, 2);
+	test_200x_init_io_state(&local_iostate, 2, 1);
+
+	Do_ADBG_BeginSubCase(c, "Start server");
+	if (!ADBG_EXPECT_TRUE(c, sock_server_init_udp(&ts, &cb)))
+		return;
+	ts_inited = true;
+	if (!ADBG_EXPECT_TRUE(c, sock_server_init_udp(&ts2, &cb)))
+		goto out;
+	ts2_inited = true;
+	if (!ADBG_EXPECT_TRUE(c, sock_server_init_udp(&ts3, &cb)))
+		goto out;
+	ts3_inited = true;
+	Do_ADBG_EndSubCase(c, "Start server");
+
+	Do_ADBG_BeginSubCase(c, "UDP Socket open");
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, xtest_teec_open_session(
+			&session, &socket_ta_uuid, NULL, &ret_orig)))
+		goto out;
+
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_udp_open(&session,
+			TEE_IP_VERSION_DC, ts.bind->host, ts.bind->port,
+			&sh, &proto_error, &ret_orig)))
+		goto out_close_session;
+
+	Do_ADBG_EndSubCase(c, "UDP Socket open");
+
+	Do_ADBG_BeginSubCase(c, "UDP Socket send");
+	blen = sizeof(buf);
+	rand_stream_read(local_iostate.write_rs, buf, blen);
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_send(&session, &sh,
+			buf, &blen, TEE_TIMEOUT_INFINITE, &ret_orig)))
+		goto out_close_session;
+	ADBG_EXPECT_COMPARE_UNSIGNED(c, blen, ==, sizeof(buf));
+	Do_ADBG_EndSubCase(c, "UDP Socket send");
+
+	Do_ADBG_BeginSubCase(c, "UDP Socket recv");
+	blen = sizeof(buf);
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_recv(&session, &sh,
+			buf, &blen, TEE_TIMEOUT_INFINITE, &ret_orig)))
+		goto out_close_session;
+	ADBG_EXPECT_COMPARE_UNSIGNED(c, blen, ==, sizeof(buf));
+	rand_stream_read(local_iostate.read_rs, buf2, blen);
+	ADBG_EXPECT_BUFFER(c, buf2, blen, buf, blen);
+	ADBG_EXPECT_TRUE(c, !server_iostate.rfail);
+	Do_ADBG_EndSubCase(c, "UDP Socket recv");
+
+	Do_ADBG_BeginSubCase(c, "UDP Socket get error");
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_get_error(&session, &sh,
+			&proto_error, &ret_orig)))
+		goto out_close_session;
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, proto_error))
+		goto out_close_session;
+	Do_ADBG_EndSubCase(c, "UDP Socket get error");
+
+	Do_ADBG_BeginSubCase(c, "UDP Socket close");
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_close(&session, &sh,
+			&ret_orig)))
+		goto out_close_session;
+	Do_ADBG_EndSubCase(c, "UDP Socket close");
+
+	Do_ADBG_BeginSubCase(c, "UDP Socket ioctl");
+
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_udp_open(&session,
+			TEE_IP_VERSION_DC, ts.bind->host, ts.bind->port,
+			&sh, &proto_error, &ret_orig)))
+		goto out_close_session;
+
+	blen = sizeof(buf);
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_ioctl(&session, &sh, 0x00F00000,
+			buf, &blen, &ret_orig)))
+		goto out_close_session;
+
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_close(&session, &sh,
+			&ret_orig)))
+		goto out_close_session;
+
+
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_udp_open(&session,
+			TEE_IP_VERSION_DC, ts.bind->host, ts.bind->port,
+			&sh, &proto_error, &ret_orig)))
+		goto out_close_session;
+
+	blen = sizeof(buf);
+	ADBG_EXPECT_TEEC_RESULT(c, TEEC_ERROR_TARGET_DEAD,
+				socket_ioctl(&session, &sh,
+					TEE_ISOCKET_PROTOCOLID_UDP << 24,
+					buf, &blen, &ret_orig));
+	TEEC_CloseSession(&session);
+
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, xtest_teec_open_session(
+			&session, &socket_ta_uuid, NULL, &ret_orig)))
+		goto out;
+
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_udp_open(&session,
+			TEE_IP_VERSION_DC, ts.bind->host, ts.bind->port,
+			&sh, &proto_error, &ret_orig)))
+		goto out_close_session;
+
+	blen = sizeof(buf);
+	ADBG_EXPECT_TEEC_RESULT(c, TEEC_ERROR_TARGET_DEAD,
+				socket_ioctl(&session, &sh, 0x32 << 24,
+				buf, &blen, &ret_orig));
+
+	Do_ADBG_EndSubCase(c, "UDP Socket ioctl");
+
+	Do_ADBG_BeginSubCase(c, "UDP Socket change port");
+
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, xtest_teec_open_session(
+			&session, &socket_ta_uuid, NULL, &ret_orig)))
+		goto out;
+
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_udp_open(&session,
+			TEE_IP_VERSION_DC, ts.bind->host, ts.bind->port,
+			&sh, &proto_error, &ret_orig)))
+		goto out_close_session;
+	sock_server_uninit(&ts);
+	ts_inited = false;
+
+	port = ts2.bind->port;
+	blen = sizeof(port);
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_ioctl(&session, &sh,
+			TEE_UDP_CHANGEPORT, &port, &blen, &ret_orig)))
+		goto out_close_session;
+
+	Do_ADBG_BeginSubCase(c, "UDP Socket send");
+	blen = sizeof(buf);
+	rand_stream_read(local_iostate.write_rs, buf, blen);
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_send(&session, &sh,
+			buf, &blen, TEE_TIMEOUT_INFINITE, &ret_orig)))
+		goto out_close_session;
+	ADBG_EXPECT_COMPARE_UNSIGNED(c, blen, ==, sizeof(buf));
+	Do_ADBG_EndSubCase(c, "UDP Socket send");
+
+	Do_ADBG_BeginSubCase(c, "UDP Socket recv");
+	blen = sizeof(buf);
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_recv(&session, &sh,
+			buf, &blen, TEE_TIMEOUT_INFINITE, &ret_orig)))
+		goto out_close_session;
+	ADBG_EXPECT_COMPARE_UNSIGNED(c, blen, ==, sizeof(buf));
+	rand_stream_read(local_iostate.read_rs, buf2, blen);
+	ADBG_EXPECT_BUFFER(c, buf2, blen, buf, blen);
+	ADBG_EXPECT_TRUE(c, !server_iostate.rfail);
+	Do_ADBG_EndSubCase(c, "UDP Socket recv");
+
+	Do_ADBG_EndSubCase(c, "UDP Socket change port");
+
+	Do_ADBG_BeginSubCase(c, "UDP Socket change addr");
+
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, xtest_teec_open_session(
+			&session, &socket_ta_uuid, NULL, &ret_orig)))
+		goto out;
+
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_udp_open(&session,
+			TEE_IP_VERSION_DC, ts2.bind->host, ts2.bind->port,
+			&sh, &proto_error, &ret_orig)))
+		goto out_close_session;
+	sock_server_uninit(&ts2);
+	ts2_inited = false;
+
+	port = ts3.bind->port;
+	blen = sizeof(port);
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_ioctl(&session, &sh,
+			TEE_UDP_CHANGEPORT, &port, &blen, &ret_orig)))
+		goto out_close_session;
+
+	blen = strlen(ts3.bind->host) + 1;
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_ioctl(&session, &sh,
+			TEE_UDP_CHANGEADDR, ts3.bind->host, &blen, &ret_orig)))
+		goto out_close_session;
+
+	Do_ADBG_BeginSubCase(c, "UDP Socket send");
+	blen = sizeof(buf);
+	rand_stream_read(local_iostate.write_rs, buf, blen);
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_send(&session, &sh,
+			buf, &blen, TEE_TIMEOUT_INFINITE, &ret_orig)))
+		goto out_close_session;
+	ADBG_EXPECT_COMPARE_UNSIGNED(c, blen, ==, sizeof(buf));
+	Do_ADBG_EndSubCase(c, "UDP Socket send");
+
+	Do_ADBG_BeginSubCase(c, "UDP Socket recv");
+	blen = sizeof(buf);
+	if (!ADBG_EXPECT_TEEC_SUCCESS(c, socket_recv(&session, &sh,
+			buf, &blen, TEE_TIMEOUT_INFINITE, &ret_orig)))
+		goto out_close_session;
+	ADBG_EXPECT_COMPARE_UNSIGNED(c, blen, ==, sizeof(buf));
+	rand_stream_read(local_iostate.read_rs, buf2, blen);
+	ADBG_EXPECT_BUFFER(c, buf2, blen, buf, blen);
+	ADBG_EXPECT_TRUE(c, !server_iostate.rfail);
+	Do_ADBG_EndSubCase(c, "UDP Socket recv");
+
+	Do_ADBG_EndSubCase(c, "UDP Socket change addr");
+
+
+out_close_session:
+	TEEC_CloseSession(&session);
+out:
+	if (ts_inited)
+		sock_server_uninit(&ts);
+	if (ts2_inited)
+		sock_server_uninit(&ts2);
+	if (ts3_inited)
+		sock_server_uninit(&ts3);
+}
+
+
+
+ADBG_CASE_DEFINE(regression, 2001, xtest_tee_test_2001,
+		"Trivial TCP iSocket API tests");
+
+ADBG_CASE_DEFINE(regression, 2002, xtest_tee_test_2002,
+		"Concurrent stressing TCP iSocket API tests");
+
+ADBG_CASE_DEFINE(regression, 2003, xtest_tee_test_2003,
+		"Timeout TCP iSocket API tests");
+
+ADBG_CASE_DEFINE(regression, 2004, xtest_tee_test_2004,
+		"UDP iSocket API tests");