From: Tomoyuki Hirose <hrstmyk8...@gmail.com>

This commit adds a test device for checking memory access. The test
device generates memory regions that covers all the legal parameter
patterns. With this device, we can check the handling of
reading/writing the MemoryRegion is correct.

Co-developed-by: CJ Chen <cjc...@igel.co.jp>
Signed-off-by: CJ Chen <cjc...@igel.co.jp>
Tested-by: CJ Chen <cjc...@igel.co.jp>
Suggested-by: Peter Maydell <peter.mayd...@linaro.org>
---
v2:
   - Fix the typo of ops size of big-l-valid.
   - Replaced the huge macro blocks with dynamic loops that fill in
     the `MemoryRegionOps` arrays at runtime.
   - Remove test cases valid.unaligned = false,impl.unaligned = true.
---
 hw/misc/Kconfig                     |   4 +
 hw/misc/memaccess-testdev.c         | 331 ++++++++++++++++++++++++++++
 hw/misc/meson.build                 |   1 +
 include/hw/misc/memaccess-testdev.h | 104 +++++++++
 4 files changed, 440 insertions(+)
 create mode 100644 hw/misc/memaccess-testdev.c
 create mode 100644 include/hw/misc/memaccess-testdev.h

diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index ec0fa5aa9f..ff7d7c65ef 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -25,6 +25,10 @@ config PCI_TESTDEV
     default y if TEST_DEVICES
     depends on PCI
 
+config MEMACCESS_TESTDEV
+    bool
+    default y if TEST_DEVICES
+
 config EDU
     bool
     default y if TEST_DEVICES
diff --git a/hw/misc/memaccess-testdev.c b/hw/misc/memaccess-testdev.c
new file mode 100644
index 0000000000..1aaa52c69f
--- /dev/null
+++ b/hw/misc/memaccess-testdev.c
@@ -0,0 +1,331 @@
+/*
+ * QEMU memory access test device
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Author: Tomoyuki HIROSE <hrstmyk8...@gmail.com>
+ *
+ * This device is used to test memory acccess, like:
+ * qemu-system-x86_64 -device memaccess-testdev,address=0x10000000
+ */
+
+#include "qemu/osdep.h"
+#include "system/address-spaces.h"
+#include "system/memory.h"
+#include "hw/qdev-core.h"
+#include "hw/qdev-properties.h"
+#include "qapi/error.h"
+#include "qemu/typedefs.h"
+#include "qom/object.h"
+#include "hw/misc/memaccess-testdev.h"
+
+typedef bool (*skip_func_ptr)(uint32_t valid_max, uint32_t valid_min,
+                              bool valid_unaligned, uint32_t impl_max,
+                              uint32_t impl_min, bool impl_unaligned);
+
+typedef struct MrOpsList {
+    const char *name;
+    MemoryRegionOps *ops_array;
+    const size_t ops_array_len;
+    const size_t offset_idx;
+    skip_func_ptr skip_fn;
+    bool is_little;
+} MrOpsList;
+
+MemoryRegionOps ops_list_little_b_valid[N_OPS_LIST_LITTLE_B_VALID];
+MemoryRegionOps ops_list_little_b_invalid[N_OPS_LIST_LITTLE_B_INVALID];
+MemoryRegionOps ops_list_little_w_valid[N_OPS_LIST_LITTLE_W_VALID];
+MemoryRegionOps ops_list_little_w_invalid[N_OPS_LIST_LITTLE_W_INVALID];
+MemoryRegionOps ops_list_little_l_valid[N_OPS_LIST_LITTLE_L_VALID];
+MemoryRegionOps ops_list_little_l_invalid[N_OPS_LIST_LITTLE_L_INVALID];
+MemoryRegionOps ops_list_little_q_valid[N_OPS_LIST_LITTLE_Q_VALID];
+MemoryRegionOps ops_list_little_q_invalid[N_OPS_LIST_LITTLE_Q_INVALID];
+MemoryRegionOps ops_list_big_b_valid[N_OPS_LIST_BIG_B_VALID];
+MemoryRegionOps ops_list_big_b_invalid[N_OPS_LIST_BIG_B_INVALID];
+MemoryRegionOps ops_list_big_w_valid[N_OPS_LIST_BIG_W_VALID];
+MemoryRegionOps ops_list_big_w_invalid[N_OPS_LIST_BIG_W_INVALID];
+MemoryRegionOps ops_list_big_l_valid[N_OPS_LIST_BIG_L_VALID];
+MemoryRegionOps ops_list_big_l_invalid[N_OPS_LIST_BIG_L_INVALID];
+MemoryRegionOps ops_list_big_q_valid[N_OPS_LIST_BIG_Q_VALID];
+MemoryRegionOps ops_list_big_q_invalid[N_OPS_LIST_BIG_Q_INVALID];
+
+static bool skip_core(uint32_t required_min, bool valid_test,
+                      uint32_t valid_max, uint32_t valid_min,
+                      bool valid_unaligned, uint32_t impl_max,
+                      uint32_t impl_min, bool impl_unaligned)
+{
+    if (valid_min != required_min) {
+        return true;
+    }
+    if (valid_test) {
+        if (!valid_unaligned) {
+            return true;
+        }
+    } else {
+        if (valid_unaligned || impl_unaligned) {
+            return true;
+        }
+    }
+    if (valid_max < valid_min) {
+        return true;
+    }
+
+    if (impl_max < impl_min) {
+        return true;
+    }
+
+    return false;
+}
+
+#define DEFINE_SKIP_VALID_INVALID_FN(NAME, REQ_MIN)                      \
+    static bool skip_##NAME##_valid(uint32_t vm, uint32_t vn, bool vu,   \
+                                    uint32_t im, uint32_t in, bool iu)   \
+    {                                                                    \
+        return skip_core(REQ_MIN, true, vm, vn, vu, im, in, iu);         \
+    }                                                                    \
+                                                                         \
+    static bool skip_##NAME##_invalid(uint32_t vm, uint32_t vn, bool vu, \
+                                      uint32_t im, uint32_t in, bool iu) \
+    {                                                                    \
+        return skip_core(REQ_MIN, false, vm, vn, vu, im, in, iu);        \
+    }
+
+DEFINE_SKIP_VALID_INVALID_FN(b, 1)
+DEFINE_SKIP_VALID_INVALID_FN(w, 2)
+DEFINE_SKIP_VALID_INVALID_FN(l, 4)
+DEFINE_SKIP_VALID_INVALID_FN(q, 8)
+
+static void testdev_init_memory_region(MemoryRegion *mr,
+                                       Object *owner,
+                                       const MemoryRegionOps *ops,
+                                       void *opaque,
+                                       const char *name,
+                                       uint64_t size,
+                                       MemoryRegion *container,
+                                       hwaddr container_offset)
+{
+    memory_region_init_io(mr, owner, ops, opaque, name, size);
+    memory_region_add_subregion(container, container_offset, mr);
+}
+
+static void testdev_init_from_mr_ops_list(MemAccessTestDev *testdev,
+                                          const MrOpsList *l)
+{
+    for (size_t i = 0; i < l->ops_array_len; i++) {
+        g_autofree gchar *name = g_strdup_printf("%s-%ld", l->name, i);
+        testdev_init_memory_region(&testdev->memory_regions[l->offset_idx + i],
+                                   OBJECT(testdev), &l->ops_array[i],
+                                   testdev->mr_data[l->offset_idx + i],
+                                   name,
+                                   MEMACCESS_TESTDEV_REGION_SIZE,
+                                   &testdev->container,
+                                   MEMACCESS_TESTDEV_REGION_SIZE *
+                                   (l->offset_idx + i));
+    }
+}
+
+#define LITTLE 1
+#define BIG    0
+#define _DEFINE_MR_OPS_LIST(_n, _ops, _len, _off, _skipfn, _is_little) \
+{                                                                      \
+    .name          = (_n),                                             \
+    .ops_array     = (_ops),                                           \
+    .ops_array_len = (_len),                                           \
+    .offset_idx    = (_off),                                           \
+    .skip_fn       = (_skipfn),                                        \
+    .is_little     = (_is_little),                                     \
+}
+
+#define DEFINE_MR_OPS_LIST(e, E, w, W, v, V)                    \
+    _DEFINE_MR_OPS_LIST(                                        \
+        #e "-" #w "-" #v,                /* .name            */ \
+        ops_list_##e##_##w##_##v,        /* .ops_array       */ \
+        N_OPS_LIST_##E##_##W##_##V,      /* .ops_array_len   */ \
+        OFF_IDX_OPS_LIST_##E##_##W##_##V,/* .offset_idx      */ \
+        skip_##w##_##v,                  /* .skip_fn         */ \
+        E /* .is_little => 1 = little endian, 0 = big endian */ \
+    )
+
+static MrOpsList mr_ops_list[] = {
+    DEFINE_MR_OPS_LIST(little, LITTLE, b, B, valid,   VALID),
+    DEFINE_MR_OPS_LIST(little, LITTLE, b, B, invalid, INVALID),
+    DEFINE_MR_OPS_LIST(little, LITTLE, w, W, valid,   VALID),
+    DEFINE_MR_OPS_LIST(little, LITTLE, w, W, invalid, INVALID),
+    DEFINE_MR_OPS_LIST(little, LITTLE, l, L, valid,   VALID),
+    DEFINE_MR_OPS_LIST(little, LITTLE, l, L, invalid, INVALID),
+    DEFINE_MR_OPS_LIST(little, LITTLE, q, Q, valid,   VALID),
+    DEFINE_MR_OPS_LIST(little, LITTLE, q, Q, invalid, INVALID),
+    DEFINE_MR_OPS_LIST(big,    BIG,    b, B, valid,   VALID),
+    DEFINE_MR_OPS_LIST(big,    BIG,    b, B, invalid, INVALID),
+    DEFINE_MR_OPS_LIST(big,    BIG,    w, W, valid,   VALID),
+    DEFINE_MR_OPS_LIST(big,    BIG,    w, W, invalid, INVALID),
+    DEFINE_MR_OPS_LIST(big,    BIG,    l, L, valid,   VALID),
+    DEFINE_MR_OPS_LIST(big,    BIG,    l, L, invalid, INVALID),
+    DEFINE_MR_OPS_LIST(big,    BIG,    q, Q, valid,   VALID),
+    DEFINE_MR_OPS_LIST(big,    BIG,    q, Q, invalid, INVALID),
+};
+#undef LITTLE
+#undef BIG
+
+static uint64_t memaccess_testdev_read_little(void *opaque, hwaddr addr,
+                                              unsigned int size)
+{
+    g_assert(addr + size < MEMACCESS_TESTDEV_REGION_SIZE);
+    void *s = (uint8_t *)opaque + addr;
+    return ldn_le_p(s, size);
+}
+
+static void memaccess_testdev_write_little(void *opaque, hwaddr addr,
+                                           uint64_t data, unsigned int size)
+{
+    g_assert(addr + size < MEMACCESS_TESTDEV_REGION_SIZE);
+    void *d = (uint8_t *)opaque + addr;
+    stn_le_p(d, size, data);
+}
+
+static uint64_t memaccess_testdev_read_big(void *opaque, hwaddr addr,
+                                           unsigned int size)
+{
+    g_assert(addr + size < MEMACCESS_TESTDEV_REGION_SIZE);
+    void *s = (uint8_t *)opaque + addr;
+    return ldn_be_p(s, size);
+}
+
+static void memaccess_testdev_write_big(void *opaque, hwaddr addr,
+                                        uint64_t data, unsigned int size)
+{
+    g_assert(addr + size < MEMACCESS_TESTDEV_REGION_SIZE);
+    void *d = (uint8_t *)opaque + addr;
+    stn_be_p(d, size, data);
+}
+
+static void fill_ops_list(MemoryRegionOps *ops,
+                          skip_func_ptr fptr,
+                          size_t ops_len,
+                          bool is_little)
+{
+    static const uint32_t sizes[] = { 1, 2, 4, 8 };
+    static const bool bools[] = { false, true };
+    int idx = 0;
+
+    for (int vMaxIdx = 0; vMaxIdx < 4; vMaxIdx++) {
+        for (int vMinIdx = 0; vMinIdx < 4; vMinIdx++) {
+            for (int vUIdx = 0; vUIdx < 2; vUIdx++) {
+                for (int iMaxIdx = 0; iMaxIdx < 4; iMaxIdx++) {
+                    for (int iMinIdx = 0; iMinIdx < 4; iMinIdx++) {
+                        for (int iUIdx = 0; iUIdx < 2; iUIdx++) {
+                            uint32_t valid_max       = sizes[vMaxIdx];
+                            uint32_t valid_min       = sizes[vMinIdx];
+                            bool     valid_unaligned = bools[vUIdx];
+                            uint32_t impl_max        = sizes[iMaxIdx];
+                            uint32_t impl_min        = sizes[iMinIdx];
+                            bool     impl_unaligned  = bools[iUIdx];
+
+                            if (!fptr(valid_max, valid_min, valid_unaligned,
+                                      impl_max, impl_min, impl_unaligned))
+                            {
+                                const MemoryRegionOps new_op = {
+                                    .read = is_little ?
+                                            memaccess_testdev_read_little :
+                                            memaccess_testdev_read_big,
+                                    .write = is_little ?
+                                             memaccess_testdev_write_little :
+                                             memaccess_testdev_write_big,
+                                    .endianness = is_little ?
+                                                  DEVICE_LITTLE_ENDIAN :
+                                                  DEVICE_BIG_ENDIAN,
+                                    .valid = {
+                                        .max_access_size = valid_max,
+                                        .min_access_size = valid_min,
+                                        .unaligned      = valid_unaligned,
+                                    },
+                                    .impl = {
+                                        .max_access_size = impl_max,
+                                        .min_access_size = impl_min,
+                                        .unaligned       = impl_unaligned,
+                                    },
+                                };
+
+                                ops[idx] = new_op;
+                                idx++;
+                                if (idx > ops_len) {
+                                    g_assert_not_reached();
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+#define N_MR_OPS_LIST (sizeof(mr_ops_list) / sizeof(MrOpsList))
+
+static void init_testdev(MemAccessTestDev *testdev)
+{
+    memory_region_init(&testdev->container, OBJECT(testdev), "memtest-regions",
+                       MEMACCESS_TESTDEV_REGION_SIZE * N_OPS_LIST);
+    testdev->mr_data = g_malloc(MEMACCESS_TESTDEV_MR_DATA_SIZE);
+
+    for (size_t i = 0; i < N_MR_OPS_LIST; i++) {
+        fill_ops_list(
+            mr_ops_list[i].ops_array,
+            mr_ops_list[i].skip_fn,
+            mr_ops_list[i].ops_array_len,
+            mr_ops_list[i].is_little
+        );
+        testdev_init_from_mr_ops_list(testdev, &mr_ops_list[i]);
+    }
+
+    memory_region_add_subregion(get_system_memory(), testdev->base,
+                                &testdev->container);
+}
+
+static void memaccess_testdev_realize(DeviceState *dev, Error **errp)
+{
+    MemAccessTestDev *d = MEM_ACCESS_TEST_DEV(dev);
+
+    if (d->base == UINT64_MAX) {
+        error_setg(errp, "base address is not assigned");
+        return;
+    }
+
+    init_testdev(d);
+}
+
+static void memaccess_testdev_unrealize(DeviceState *dev)
+{
+    MemAccessTestDev *d = MEM_ACCESS_TEST_DEV(dev);
+    g_free(d->mr_data);
+}
+
+static Property memaccess_testdev_props[] = {
+    DEFINE_PROP_UINT64("address", MemAccessTestDev, base, UINT64_MAX),
+};
+
+static void memaccess_testdev_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = memaccess_testdev_realize;
+    dc->unrealize = memaccess_testdev_unrealize;
+    device_class_set_props_n(dc,
+                             memaccess_testdev_props,
+                             ARRAY_SIZE(memaccess_testdev_props));
+    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static const TypeInfo memaccess_testdev_info = {
+    .name = TYPE_MEM_ACCESS_TEST_DEV,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(MemAccessTestDev),
+    .class_init = memaccess_testdev_class_init,
+};
+
+static void memaccess_testdev_register_types(void)
+{
+    type_register_static(&memaccess_testdev_info);
+}
+
+type_init(memaccess_testdev_register_types)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 6d47de482c..f06568aaed 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -4,6 +4,7 @@ system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: 
files('vmcoreinfo.c'))
 system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c'))
 system_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c'))
 system_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c'))
+system_ss.add(when: 'CONFIG_MEMACCESS_TESTDEV', if_true: 
files('memaccess-testdev.c'))
 system_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c'))
 system_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c'))
 system_ss.add(when: 'CONFIG_LED', if_true: files('led.c'))
diff --git a/include/hw/misc/memaccess-testdev.h 
b/include/hw/misc/memaccess-testdev.h
new file mode 100644
index 0000000000..c1b17297a2
--- /dev/null
+++ b/include/hw/misc/memaccess-testdev.h
@@ -0,0 +1,104 @@
+/*
+ * QEMU memory access test device header
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Author: Tomoyuki HIROSE <hrstmyk8...@gmail.com>
+ */
+
+#ifndef HW_MISC_MEMACCESS_TESTDEV_H
+#define HW_MISC_MEMACCESS_TESTDEV_H
+
+#include "system/memory.h"
+#include "hw/qdev-core.h"
+
+#define TYPE_MEM_ACCESS_TEST_DEV "memaccess-testdev"
+
+#define MEMACCESS_TESTDEV_REGION_SIZE 32
+
+#define N_OPS_LIST_LITTLE_B_VALID   80
+#define N_OPS_LIST_LITTLE_B_INVALID 40
+#define N_OPS_LIST_LITTLE_W_VALID   60
+#define N_OPS_LIST_LITTLE_W_INVALID 30
+#define N_OPS_LIST_LITTLE_L_VALID   40
+#define N_OPS_LIST_LITTLE_L_INVALID 20
+#define N_OPS_LIST_LITTLE_Q_VALID   20
+#define N_OPS_LIST_LITTLE_Q_INVALID 10
+#define N_OPS_LIST_BIG_B_VALID      80
+#define N_OPS_LIST_BIG_B_INVALID    40
+#define N_OPS_LIST_BIG_W_VALID      60
+#define N_OPS_LIST_BIG_W_INVALID    30
+#define N_OPS_LIST_BIG_L_VALID      40
+#define N_OPS_LIST_BIG_L_INVALID    20
+#define N_OPS_LIST_BIG_Q_VALID      20
+#define N_OPS_LIST_BIG_Q_INVALID    10
+
+#define N_OPS_LIST \
+    (N_OPS_LIST_LITTLE_B_VALID   + \
+     N_OPS_LIST_LITTLE_B_INVALID + \
+     N_OPS_LIST_LITTLE_W_VALID   + \
+     N_OPS_LIST_LITTLE_W_INVALID + \
+     N_OPS_LIST_LITTLE_L_VALID   + \
+     N_OPS_LIST_LITTLE_L_INVALID + \
+     N_OPS_LIST_LITTLE_Q_VALID   + \
+     N_OPS_LIST_LITTLE_Q_INVALID + \
+     N_OPS_LIST_BIG_B_VALID      + \
+     N_OPS_LIST_BIG_B_INVALID    + \
+     N_OPS_LIST_BIG_W_VALID      + \
+     N_OPS_LIST_BIG_W_INVALID    + \
+     N_OPS_LIST_BIG_L_VALID      + \
+     N_OPS_LIST_BIG_L_INVALID    + \
+     N_OPS_LIST_BIG_Q_VALID      + \
+     N_OPS_LIST_BIG_Q_INVALID)
+
+#define OFF_IDX_OPS_LIST_LITTLE_B_VALID \
+    (0)
+#define OFF_IDX_OPS_LIST_LITTLE_B_INVALID \
+    (OFF_IDX_OPS_LIST_LITTLE_B_VALID + N_OPS_LIST_LITTLE_B_VALID)
+#define OFF_IDX_OPS_LIST_LITTLE_W_VALID \
+    (OFF_IDX_OPS_LIST_LITTLE_B_INVALID + N_OPS_LIST_LITTLE_B_INVALID)
+#define OFF_IDX_OPS_LIST_LITTLE_W_INVALID \
+    (OFF_IDX_OPS_LIST_LITTLE_W_VALID + N_OPS_LIST_LITTLE_W_VALID)
+#define OFF_IDX_OPS_LIST_LITTLE_L_VALID \
+    (OFF_IDX_OPS_LIST_LITTLE_W_INVALID + N_OPS_LIST_LITTLE_W_INVALID)
+#define OFF_IDX_OPS_LIST_LITTLE_L_INVALID \
+    (OFF_IDX_OPS_LIST_LITTLE_L_VALID + N_OPS_LIST_LITTLE_L_VALID)
+#define OFF_IDX_OPS_LIST_LITTLE_Q_VALID \
+    (OFF_IDX_OPS_LIST_LITTLE_L_INVALID + N_OPS_LIST_LITTLE_L_INVALID)
+#define OFF_IDX_OPS_LIST_LITTLE_Q_INVALID \
+    (OFF_IDX_OPS_LIST_LITTLE_Q_VALID + N_OPS_LIST_LITTLE_Q_VALID)
+#define OFF_IDX_OPS_LIST_BIG_B_VALID \
+    (OFF_IDX_OPS_LIST_LITTLE_Q_INVALID + N_OPS_LIST_LITTLE_Q_INVALID)
+#define OFF_IDX_OPS_LIST_BIG_B_INVALID \
+    (OFF_IDX_OPS_LIST_BIG_B_VALID + N_OPS_LIST_BIG_B_VALID)
+#define OFF_IDX_OPS_LIST_BIG_W_VALID \
+    (OFF_IDX_OPS_LIST_BIG_B_INVALID + N_OPS_LIST_BIG_B_INVALID)
+#define OFF_IDX_OPS_LIST_BIG_W_INVALID \
+    (OFF_IDX_OPS_LIST_BIG_W_VALID + N_OPS_LIST_BIG_W_VALID)
+#define OFF_IDX_OPS_LIST_BIG_L_VALID \
+    (OFF_IDX_OPS_LIST_BIG_W_INVALID + N_OPS_LIST_BIG_W_INVALID)
+#define OFF_IDX_OPS_LIST_BIG_L_INVALID \
+    (OFF_IDX_OPS_LIST_BIG_L_VALID + N_OPS_LIST_BIG_L_VALID)
+#define OFF_IDX_OPS_LIST_BIG_Q_VALID \
+    (OFF_IDX_OPS_LIST_BIG_L_INVALID + N_OPS_LIST_BIG_L_INVALID)
+#define OFF_IDX_OPS_LIST_BIG_Q_INVALID \
+    (OFF_IDX_OPS_LIST_BIG_Q_VALID + N_OPS_LIST_BIG_Q_VALID)
+
+typedef uint8_t MrData[MEMACCESS_TESTDEV_REGION_SIZE];
+#define MEMACCESS_TESTDEV_MR_DATA_SIZE (sizeof(MrData) * N_OPS_LIST)
+
+typedef DeviceClass MemAccessTestDevClass;
+typedef struct MemAccessTestDev {
+    /* Private */
+    DeviceState parent_obj;
+    /* Public */
+    MemoryRegion container;
+    MemoryRegion memory_regions[N_OPS_LIST]; /* test memory regions */
+    uint64_t base;                           /* map base address */
+    MrData *mr_data;                         /* memory region data array */
+} MemAccessTestDev;
+
+#define MEM_ACCESS_TEST_DEV(obj)                                    \
+    OBJECT_CHECK(MemAccessTestDev, obj, TYPE_MEM_ACCESS_TEST_DEV)
+
+#endif
-- 
2.25.1


Reply via email to