From: jprx <[email protected]>

The Exynos 4210 UART FIFO implementation will overflow if a large buffer
is sent via a chardev. This test demonstrates the issue by sending an
input message that is 4x the size of the UART0 FIFO length.

A correct implementation will return the complete message, and an
incorrect one will timeout. This is because the FIFO overflows when
receiving data from the chardev, skipping over the first part of the
input message, resulting in us not reading the full message and timing
out.

Signed-off-by: Joseph Ravichandran <[email protected]>
Message-id: [email protected]
Reviewed-by: Peter Maydell <[email protected]>
Signed-off-by: Peter Maydell <[email protected]>
---
 tests/qtest/exynos4210_uart_fifo-test.c | 86 +++++++++++++++++++++++++
 tests/qtest/meson.build                 |  4 ++
 2 files changed, 90 insertions(+)
 create mode 100644 tests/qtest/exynos4210_uart_fifo-test.c

diff --git a/tests/qtest/exynos4210_uart_fifo-test.c 
b/tests/qtest/exynos4210_uart_fifo-test.c
new file mode 100644
index 00000000000..54c09a6de72
--- /dev/null
+++ b/tests/qtest/exynos4210_uart_fifo-test.c
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "qemu/osdep.h"
+#include "libqtest.h"
+
+/* defs from exynos4210.c */
+#define UART0_BASE_ADDR     0x13800000
+#define UART0_FIFO_SIZE     256
+#define UFCON      0x0008 /* FIFO Control             */
+#define UTRSTAT    0x0010 /* Tx/Rx Status             */
+#define URXH       0x0024 /* Receive Buffer           */
+#define UFCON_FIFO_ENABLE                    0x1
+#define UTRSTAT_Rx_BUFFER_DATA_READY         0x1
+
+#define TEST_BUFFER_LEN        ((4 * UART0_FIFO_SIZE))
+
+static bool uart_wait_for_flag(QTestState *qts, uint32_t event_addr,
+                               uint32_t flag)
+{
+    while (true) {
+        if ((qtest_readl(qts, event_addr) & flag)) {
+            return true;
+        }
+        g_usleep(1000);
+    }
+
+    return false;
+}
+
+static void uart_receive_string(QTestState *qts, int sock_fd, const char *in,
+                                char *out)
+{
+    size_t i, in_len = strlen(in);
+
+    g_assert_true(send(sock_fd, in, in_len, 0) == in_len);
+    for (i = 0; i < in_len; i++) {
+        g_assert_true(uart_wait_for_flag(qts,
+            UART0_BASE_ADDR + UTRSTAT, UTRSTAT_Rx_BUFFER_DATA_READY));
+        out[i] = qtest_readl(qts, UART0_BASE_ADDR + URXH);
+    }
+    out[i] = '\0';
+}
+
+static void enable_uart_fifo(QTestState *qts)
+{
+    qtest_writel(qts, UART0_BASE_ADDR + UFCON, UFCON_FIFO_ENABLE);
+}
+
+/*
+ * If the FIFO overflows when receiving data from the chardev, early input
+ * chunks will be lost, and this will timeout waiting to read the full
+ * buffer. If the FIFO behaves correctly, we will receive the full buffer.
+ */
+static void test_recv_large_str(void)
+{
+    int sock_fd;
+    char *sendbuf, *recvbuf;
+    QTestState *qts = qtest_init_with_serial("-M smdkc210", &sock_fd);
+
+    enable_uart_fifo(qts);
+
+    sendbuf = g_new0(char, TEST_BUFFER_LEN);
+    recvbuf = g_new0(char, TEST_BUFFER_LEN);
+    memset(sendbuf, 'A', TEST_BUFFER_LEN);
+    sendbuf[TEST_BUFFER_LEN - 1] = '\x00';
+
+    g_assert_true(0 == strlen(recvbuf));
+    g_assert_true(TEST_BUFFER_LEN - 1 == strlen(sendbuf));
+
+    uart_receive_string(qts, sock_fd, sendbuf, recvbuf);
+
+    g_assert_true(TEST_BUFFER_LEN - 1 == strlen(recvbuf));
+    g_assert_true(strcmp(sendbuf, recvbuf) == 0);
+
+    close(sock_fd);
+
+    qtest_quit(qts);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+    g_test_set_nonfatal_assertions();
+
+    qtest_add_func("exynos4210/uart/receive_large_str", test_recv_large_str);
+    return g_test_run();
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index a35ae79b03d..3aed0c3a00a 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -249,6 +249,9 @@ qtests_stm32l4x5 = \
    'stm32l4x5_gpio-test',
    'stm32l4x5_usart-test']
 
+qtests_exynos4210 = \
+  ['exynos4210_uart_fifo-test']
+
 qtests_arm = \
   (config_all_devices.has_key('CONFIG_MPS2') ? ['sse-timer-test'] : []) + \
   (config_all_devices.has_key('CONFIG_CMSDK_APB_DUALTIMER') ? 
['cmsdk-apb-dualtimer-test'] : []) + \
@@ -283,6 +286,7 @@ qtests_aarch64 = \
    config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test', 
'tpm-tis-i2c-swtpm-test'] : []) + \
   (config_all_devices.has_key('CONFIG_ASPEED_SOC') ? qtests_aspeed64 : []) + \
   (config_all_devices.has_key('CONFIG_NPCM8XX') ? qtests_npcm8xx : []) + \
+  (config_all_devices.has_key('CONFIG_EXYNOS4') ? qtests_exynos4210 : []) + \
   (config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') and
    config_all_devices.has_key('CONFIG_ARM_VIRT') ?
    ['iommu-smmuv3-test'] : []) + \
-- 
2.43.0


Reply via email to