summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NOTICE10
-rw-r--r--dts/sa8155-vm-virtio-clk-ut.dtsi148
-rw-r--r--virtio_clk/Makefile16
-rw-r--r--virtio_clk/fault_injection_virtio_clk_test.c271
-rw-r--r--virtio_clk/kprobe_virtio_clk_ut.c196
-rw-r--r--virtio_clk/run_fault_injection.sh102
-rw-r--r--virtio_clk/virtio_clk_unit_test.c521
7 files changed, 1264 insertions, 0 deletions
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..7b6f3e3
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,10 @@
+Copyright (c) 2019, The Linux Foundation. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 and
+only 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.
diff --git a/dts/sa8155-vm-virtio-clk-ut.dtsi b/dts/sa8155-vm-virtio-clk-ut.dtsi
new file mode 100644
index 0000000..b85a488
--- /dev/null
+++ b/dts/sa8155-vm-virtio-clk-ut.dtsi
@@ -0,0 +1,148 @@
+/* Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+
+/*
+ * virt-clk-ut device tree in the 64 bits version; It supports virtio clock
+ * and reset controller mapping for the unit test kernel module of virtio-clk.
+ */
+
+&soc {
+ qcom,virtio_clock_ut {
+ compatible = "qcom,virtio-clock-unit-test";
+ clock-names =
+ "unit_test_clk_1",
+ "unit_test_clk_2",
+ "unit_test_clk_3",
+ "unit_test_clk_4",
+ "unit_test_clk_5",
+ "unit_test_clk_6",
+ "unit_test_clk_7",
+ "unit_test_clk_8",
+ "unit_test_clk_9",
+ "unit_test_clk_10",
+ "unit_test_clk_11",
+ "unit_test_clk_12",
+ "unit_test_clk_13",
+ "unit_test_clk_14",
+ "unit_test_clk_15",
+ "unit_test_clk_16",
+ "unit_test_clk_17",
+ "unit_test_clk_18",
+ "unit_test_clk_19",
+ "unit_test_clk_20",
+ "unit_test_clk_21",
+ "unit_test_clk_22",
+ "unit_test_clk_23",
+ "unit_test_clk_24",
+ "unit_test_clk_25",
+ "unit_test_clk_26",
+ "unit_test_clk_27",
+ "unit_test_clk_28",
+ "unit_test_clk_29",
+ "unit_test_clk_30",
+ "unit_test_clk_31",
+ "unit_test_clk_32",
+ "unit_test_clk_33",
+ "unit_test_clk_34",
+ "unit_test_clk_35",
+ "unit_test_clk_36",
+ "unit_test_clk_37",
+ "unit_test_clk_38",
+ "unit_test_clk_39",
+ "unit_test_clk_40",
+ "unit_test_clk_41",
+ "unit_test_clk_42",
+ "unit_test_clk_43",
+ "unit_test_clk_44",
+ "unit_test_clk_45",
+ "unit_test_clk_46",
+ "unit_test_clk_47",
+ "unit_test_clk_48",
+ "unit_test_clk_49",
+ "unit_test_clk_50",
+ "unit_test_clk_51",
+ "unit_test_clk_52";
+ clocks =
+ <&clock_virt GCC_QUPV3_WRAP0_S0_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP0_S1_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP0_S2_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP0_S3_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP0_S4_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP0_S5_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP0_S6_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP0_S7_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP1_S0_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP1_S1_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP1_S2_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP1_S3_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP1_S4_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP1_S5_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP2_S0_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP2_S1_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP2_S2_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP2_S3_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP2_S4_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP2_S5_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP_0_M_AHB_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP_0_S_AHB_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP_1_M_AHB_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP_1_S_AHB_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP_2_M_AHB_CLK>,
+ <&clock_virt GCC_QUPV3_WRAP_2_S_AHB_CLK>,
+ <&clock_virt GCC_USB30_PRIM_MASTER_CLK>,
+ <&clock_virt GCC_CFG_NOC_USB3_PRIM_AXI_CLK>,
+ <&clock_virt GCC_AGGRE_USB3_PRIM_AXI_CLK>,
+ <&clock_virt GCC_USB30_PRIM_MOCK_UTMI_CLK>,
+ <&clock_virt GCC_USB30_PRIM_SLEEP_CLK>,
+ <&clock_virt GCC_USB3_SEC_CLKREF_CLK>,
+ <&clock_virt GCC_USB3_PRIM_PHY_AUX_CLK>,
+ <&clock_virt GCC_USB3_PRIM_PHY_PIPE_CLK>,
+ <&clock_virt GCC_USB3_PRIM_CLKREF_CLK>,
+ <&clock_virt GCC_USB3_PRIM_PHY_COM_AUX_CLK>,
+ <&clock_virt GCC_USB30_SEC_MASTER_CLK>,
+ <&clock_virt GCC_CFG_NOC_USB3_SEC_AXI_CLK>,
+ <&clock_virt GCC_AGGRE_USB3_SEC_AXI_CLK>,
+ <&clock_virt GCC_USB30_SEC_MOCK_UTMI_CLK>,
+ <&clock_virt GCC_USB30_SEC_SLEEP_CLK>,
+ <&clock_virt GCC_USB3_SEC_PHY_AUX_CLK>,
+ <&clock_virt GCC_USB3_SEC_PHY_PIPE_CLK>,
+ <&clock_virt GCC_USB3_SEC_PHY_COM_AUX_CLK>,
+ <&clock_virt GCC_PCIE_0_MSTR_AXI_CLK>,
+ <&clock_virt GCC_PCIE_0_SLV_AXI_CLK>,
+ <&clock_virt_scc SCC_QUPV3_SE0_CLK>,
+ <&clock_virt_scc SCC_QUPV3_SE1_CLK>,
+ <&clock_virt_scc SCC_QUPV3_SE2_CLK>,
+ <&clock_virt_scc SCC_QUPV3_SE3_CLK>,
+ <&clock_virt_scc SCC_QUPV3_M_HCLK_CLK>,
+ <&clock_virt_scc SCC_QUPV3_S_HCLK_CLK>;
+ reset-names =
+ "unit_test_reset_controller_1",
+ "unit_test_reset_controller_2",
+ "unit_test_reset_controller_3",
+ "unit_test_reset_controller_4",
+ "unit_test_reset_controller_5",
+ "unit_test_reset_controller_6",
+ "unit_test_reset_controller_7",
+ "unit_test_reset_controller_8";
+ resets =
+ <&clock_virt GCC_QUSB2PHY_PRIM_BCR>,
+ <&clock_virt GCC_QUSB2PHY_SEC_BCR>,
+ <&clock_virt GCC_USB3_PHY_PRIM_BCR>,
+ <&clock_virt GCC_USB3_DP_PHY_PRIM_BCR>,
+ <&clock_virt GCC_USB3_PHY_SEC_BCR>,
+ <&clock_virt GCC_USB3PHY_PHY_SEC_BCR>,
+ <&clock_virt GCC_USB30_PRIM_BCR>,
+ <&clock_virt GCC_USB30_SEC_BCR>;
+ status = "okay";
+ };
+};
diff --git a/virtio_clk/Makefile b/virtio_clk/Makefile
new file mode 100644
index 0000000..adbb83c
--- /dev/null
+++ b/virtio_clk/Makefile
@@ -0,0 +1,16 @@
+obj-m := virtio_clk_unit_test.o
+obj-m += kprobe_virtio_clk_ut.o
+obj-m += fault_injection_virtio_clk_test.o
+
+SRC := $(shell pwd)
+
+all:
+ $(MAKE) -C $(KERNEL_SRC) M=$(SRC)
+
+modules_install:
+ $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install
+
+clean:
+ rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c
+ rm -f Module.markers Module.symvers modules.order
+ rm -rf .tmp_versions Modules.symvers
diff --git a/virtio_clk/fault_injection_virtio_clk_test.c b/virtio_clk/fault_injection_virtio_clk_test.c
new file mode 100644
index 0000000..47c336d
--- /dev/null
+++ b/virtio_clk/fault_injection_virtio_clk_test.c
@@ -0,0 +1,271 @@
+/* Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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 <linux/clk.h>
+#include <linux/clk/qcom.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/kernel.h>
+#include <linux/reset.h>
+#include <linux/kprobes.h>
+#include <asm/ptrace.h>
+#include <linux/ktf.h>
+
+KTF_INIT();
+
+#define DRV_NAME "virt_clk_fault_inject_test"
+
+static struct device *dev_fi;
+static struct clk *clk_fi;
+static struct reset_control *rst_fi;
+
+TEST(fault_injection_1, Inject_clk_get)
+{
+ tlog(T_INFO, "injecting error in virtio clock driver...\n");
+ clk_fi = clk_get(dev_fi, "unit_test_clk_1");
+ tlog(T_INFO, "Clock unit_test_clk_1 get return 0x%x\n", clk_fi);
+ if (!IS_ERR_OR_NULL(clk_fi))
+ ASSERT_FALSE_GOTO(true, err);
+ EXPECT_TRUE(true);
+ clk_put(clk_fi);
+
+ return;
+
+err:
+ terr("fault injection for clk_get failed\n");
+}
+
+TEST(fault_injection_2, Inject_clk_prepare)
+{
+ int ret = 0;
+
+ tlog(T_INFO, "injecting error in virtio clock driver...\n");
+
+ clk_fi = clk_get(dev_fi, "unit_test_clk_1");
+ if (!IS_ERR_OR_NULL(clk_fi)) {
+ ret = clk_prepare(clk_fi);
+ tlog(T_INFO, "clk_prepare return = %d\n", ret);
+ ASSERT_INT_NE_GOTO(0, ret, err);
+ clk_put(clk_fi);
+ } else {
+ tlog(T_INFO, "Clock unit_test_clk_1 get failed\n");
+ }
+ return;
+
+err:
+ terr("fault injection for clk_prepare failed\n");
+ clk_put(clk_fi);
+}
+
+TEST(fault_injection_3, Inject_clk_set_rate)
+{
+ int ret = 0;
+
+ tlog(T_INFO, "injecting error in virtio clock driver...\n");
+ clk_fi = clk_get(dev_fi, "unit_test_clk_1");
+ if (!IS_ERR_OR_NULL(clk_fi)) {
+ ret = clk_set_rate(clk_fi, 400000);
+ tlog(T_INFO, "clk unit_test_clk_1 set rate return %d\n", ret);
+ ASSERT_INT_NE_GOTO(0, ret, err);
+ clk_put(clk_fi);
+ } else {
+ tlog(T_INFO, "Clock unit_test_clk_1 get failed\n");
+ }
+ return;
+
+err:
+ terr("fault injection for clk_set_rate failed\n");
+ clk_put(clk_fi);
+}
+
+TEST(fault_injection_4, Inject_clk_get_rate)
+{
+ int ret = 0;
+ unsigned long rate_get;
+
+ tlog(T_INFO, "injecting error in virtio clock driver...\n");
+ clk_fi = clk_get(dev_fi, "unit_test_clk_1");
+ if (!IS_ERR_OR_NULL(clk_fi)) {
+ ret = clk_prepare_enable(clk_fi);
+ EXPECT_INT_EQ(0, ret);
+ ret = clk_set_rate(clk_fi, 400000);
+ tlog(T_INFO, "clk unit_test_clk_1 set rate 400000 return %d\n", ret);
+ rate_get = clk_get_rate(clk_fi);
+ tlog(T_INFO, "clk unit_test_clk_1 get rate return %lu\n", rate_get);
+ ASSERT_INT_GE_GOTO(1, rate_get, err);
+ clk_put(clk_fi);
+ } else {
+ terr("Clock unit_test_clk_1 get failed\n");
+ }
+
+ return;
+
+err:
+ terr("fault injection for clk_get_rate failed\n");
+ clk_put(clk_fi);
+}
+
+TEST(fault_injection_5, Inject_clk_round_rate)
+{
+
+ long round_rate;
+
+ tlog(T_INFO, "injecting error in virtio clock driver...\n");
+ clk_fi = clk_get(dev_fi, "unit_test_clk_1");
+ if (!IS_ERR_OR_NULL(clk_fi)) {
+ round_rate = clk_round_rate(clk_fi, 19200000);
+ tlog(T_INFO, "clk unit_test_clk_1 round rate 19200000 return %ld\n", round_rate);
+ ASSERT_INT_GE_GOTO(0, round_rate, err);
+ clk_put(clk_fi);
+ } else {
+ terr("Clock unit_test_clk_1 get failed\n");
+ }
+
+ return;
+
+err:
+ terr("fault injection for clk_round_rate failed\n");
+ clk_put(clk_fi);
+}
+
+TEST(fault_injection_6, Inject_clk_set_flags)
+{
+ int ret = 0;
+
+ tlog(T_INFO, "injecting error in virtio clock driver...\n");
+ clk_fi = clk_get(dev_fi, "unit_test_clk_45");
+ ret = clk_set_flags(clk_fi, CLKFLAG_RETAIN_PERIPH);
+ tlog(T_INFO, "clock unit_test_clk_45 set flag CLKFLAG_RETAIN_PERIPH return %d\n", ret);
+ if (!IS_ERR_OR_NULL(clk_fi)) {
+ ASSERT_INT_NE_GOTO(0, clk_set_flags(clk_fi, CLKFLAG_NORETAIN_MEM), err);
+ clk_put(clk_fi);
+ } else {
+ tlog(T_INFO, "Clock unit_test_clk_45 get failed\n");
+ }
+ return;
+
+err:
+ terr("fault injection for clk_set_flags failed\n");
+ clk_put(clk_fi);
+}
+
+TEST(fault_injection_7, Inject_reset_control_reset)
+{
+ int ret = 0;
+
+ rst_fi = reset_control_get(dev_fi, "unit_test_reset_controller_1");
+ if (!IS_ERR_OR_NULL(rst_fi)) {
+ ret = reset_control_reset(rst_fi);
+ ASSERT_INT_NE_GOTO(0, ret, err);
+ reset_control_put(rst_fi);
+ } else {
+ tlog(T_INFO, "reset controller unit_test_reset_controller_1 get failed\n");
+ }
+ return;
+
+err:
+ terr("fault injection for reset_control_reset failed\n");
+ reset_control_put(rst_fi);
+}
+
+TEST(fault_injection_8, Inject_reset_control_assert)
+{
+ int ret = 0;
+
+ rst_fi = reset_control_get(dev_fi, "unit_test_reset_controller_1");
+ if (!IS_ERR_OR_NULL(rst_fi)) {
+ ret = reset_control_assert(rst_fi);
+ ASSERT_INT_NE_GOTO(0, ret, err);
+ reset_control_deassert(rst_fi);
+ reset_control_put(rst_fi);
+ } else {
+ tlog(T_INFO, "reset controller unit_test_reset_controller_1 get failed\n");
+ }
+
+err:
+ terr("fault injection for reset_control_assert failed\n");
+ reset_control_put(rst_fi);
+}
+
+static const struct of_device_id virt_clk_test_dt_ids[] = {
+ { .compatible = "qcom,virtio-clock-unit-test" },
+ {}
+};
+
+static int virt_clk_fault_inject_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *id;
+ tlog(T_INFO, "virt_clk_kprobe_test_probe\n");
+ id = of_match_device(virt_clk_test_dt_ids, &pdev->dev);
+ if (!id) {
+ terr("No matching device found\n");
+ return -ENODEV;
+ }
+ tlog(T_INFO, "of_match_device, &pdev->dev = 0x%x\n", &pdev->dev);
+
+ dev_fi = &pdev->dev;
+
+ return 0;
+}
+
+static int virt_clk_fault_inject_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static void add_tests(void)
+{
+ ADD_TEST(Inject_clk_get);
+ ADD_TEST(Inject_clk_prepare);
+ ADD_TEST(Inject_clk_set_rate);
+ ADD_TEST(Inject_clk_get_rate);
+ ADD_TEST(Inject_clk_round_rate);
+ ADD_TEST(Inject_clk_set_flags);
+ ADD_TEST(Inject_reset_control_reset);
+ ADD_TEST(Inject_reset_control_assert);
+}
+
+static struct platform_driver virt_clk_test_driver = {
+ .probe = virt_clk_fault_inject_probe,
+ .remove = virt_clk_fault_inject_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = virt_clk_test_dt_ids,
+ },
+};
+
+static int __init virtio_clk_fault_inject_init(void)
+{
+ int ret;
+ tlog(T_INFO, "Fault injection kernel module for virtio clock initialising\n");
+
+ add_tests();
+
+ ret = platform_driver_register(&virt_clk_test_driver);
+ if (ret)
+ terr("Error registering platform driver\n");
+
+ tlog(T_INFO, "Driver initialized\n");
+ return ret;
+}
+
+static void __exit virtio_clk_fault_inject_exit(void)
+{
+ platform_driver_unregister(&virt_clk_test_driver);
+ tlog(T_INFO, "virtio_clk fault injection kernel module Unloaded\n");
+ KTF_CLEANUP();
+}
+
+module_init(virtio_clk_fault_inject_init);
+module_exit(virtio_clk_fault_inject_exit);
+MODULE_LICENSE("GPL v2");
diff --git a/virtio_clk/kprobe_virtio_clk_ut.c b/virtio_clk/kprobe_virtio_clk_ut.c
new file mode 100644
index 0000000..f25673c
--- /dev/null
+++ b/virtio_clk/kprobe_virtio_clk_ut.c
@@ -0,0 +1,196 @@
+/* Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kprobes.h>
+#include <asm/ptrace.h>
+
+#define MAX_PROBES 6
+
+static char *symbol[MAX_PROBES];
+static int symbol_num;
+static int offset[MAX_PROBES];
+
+module_param_array(symbol, charp, &symbol_num, 0);
+MODULE_PARM_DESC(symbol, "The probe symbol for the kprobe injection.");
+
+module_param_array(offset, int, NULL, 0);
+MODULE_PARM_DESC(symbol, "The offset value starting from symbol for the kprobe injection.");
+
+struct probe_point {
+ char *api;
+ int offset;
+ int reg;
+ int value;
+};
+
+static const struct probe_point probe_lists[] = {
+ { "of_clk_hw_virtio_get", 0x134, 8, -1 },
+ { "of_clk_hw_virtio_get", 0x6c, 15, 1000 },
+ { "virtio_clk_prepare", 0x920, 10, 1 },
+ { "virtio_clk_prepare", 0xa14, 9, 0 },
+ { "virtio_clk_prepare", 0xab4, 9, 0 },
+ { "virtio_clk_set_rate", 0xcc4, 10, 1 },
+ { "virtio_clk_set_rate", 0xdb8, 9, 0 },
+ { "virtio_clk_set_rate", 0xe58, 9, 0 },
+ { "virtio_clk_get_rate", 0x934, 22, 1 },
+ { "virtio_clk_get_rate", 0xa28, 9, 0 },
+ { "virtio_clk_get_rate", 0xac4, 9, 0 },
+ { "virtio_clk_get_rate", 0xb50, 12, 1 },
+ { "virtio_clk_round_rate", 0xcc0, 10, -1 },
+ { "virtio_clk_round_rate", 0xdb4, 9, 0 },
+ { "virtio_clk_round_rate", 0xe50, 9, 0 },
+ { "virtio_clk_round_rate", 0xedc, 12, 1 },
+ { "virtio_clk_set_flags", 0xcb0, 10, -1 },
+ { "virtio_clk_set_flags", 0xda4, 9, 0 },
+ { "virtio_clk_set_flags", 0xe40, 9, 0 },
+ { "__virtio_reset", 0xd68, 10, -1 },
+ { "__virtio_reset", 0xe48, 9, 0 },
+ { "__virtio_reset", 0xed4, 9, 0 },
+};
+
+static int kp_index, kp2_index;
+static int kp_value, kp2_value;
+
+/* For each probe you need to allocate a kprobe structure */
+static struct kprobe kp;
+static struct kprobe kp2;
+
+static struct kprobe *kps[2] = {&kp, &kp2};
+
+/* kprobe pre_handler: called just before the probed instruction is executed */
+static int kp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ printk(KERN_INFO "%s: p->addr=0x%pK, x[%d]=0x%lx\n", __func__,
+ p->addr, kp_index, regs->user_regs.regs[kp_index]);
+ regs->user_regs.regs[kp_index] = kp_value;
+ /* A dump_stack() here will give a stack backtrace */
+ dump_stack();
+ return 0;
+}
+
+static int kp2_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ printk(KERN_INFO "%s: p->addr=0x%pK, x[%d]=0x%lx\n", __func__,
+ p->addr, kp2_index, regs->user_regs.regs[kp2_index]);
+ regs->user_regs.regs[kp2_index] = kp2_value;
+ /* A dump_stack() here will give a stack backtrace */
+ dump_stack();
+ return 0;
+}
+
+/* kprobe post_handler: called after the probed instruction is executed */
+static void kp_post_handler(struct kprobe *p, struct pt_regs *regs,
+ unsigned long flags)
+{
+ printk(KERN_INFO "%s: p->addr=0x%pK, x[%d]=0x%lx\n",
+ __func__, p->addr, kp_index, regs->user_regs.regs[kp_index]);
+ regs->user_regs.regs[kp_index] = kp_value;
+}
+
+static void kp2_post_handler(struct kprobe *p, struct pt_regs *regs,
+ unsigned long flags)
+{
+ printk(KERN_INFO "%s: p->addr=0x%pK, x[%d]=0x%lx, flags=%ld\n",
+ __func__, p->addr, kp2_index, regs->user_regs.regs[kp2_index], flags);
+}
+
+/*
+ * fault_handler: this is called if an exception is generated for any
+ * instruction within the pre- or post-handler, or when Kprobes
+ * single-steps the probed instruction.
+ */
+static int kp_fault_handler(struct kprobe *p, struct pt_regs *regs, int trapnr)
+{
+ printk(KERN_INFO "fault_handler: p->addr = 0x%p, trap #%d\n",
+ p->addr, trapnr);
+ /* Return 0 because we don't handle the fault. */
+ return 0;
+}
+
+static int __init kprobe_init(void)
+{
+ int i, j, ret, list_len;
+ kprobe_opcode_t *symbol_addr;
+
+ list_len = sizeof(probe_lists)/sizeof(struct probe_point);
+
+ printk(KERN_INFO "symbol numbers = %d, list number = %d\n", symbol_num, list_len);
+ for (i=0; i<symbol_num; i++) {
+ for (j=0; j<list_len; j++) {
+ if (!strcmp(symbol[i], probe_lists[j].api) && (offset[i] == probe_lists[j].offset)) {
+ if (i == 0) {
+ kp.symbol_name = probe_lists[j].api;
+ kp.offset = probe_lists[j].offset;
+ kp.pre_handler = kp_pre_handler;
+ kp.post_handler = kp_post_handler;
+ kp.fault_handler = kp_fault_handler;
+ kp_index = probe_lists[j].reg;
+ kp_value = probe_lists[j].value;
+ }
+ if (i == 1) {
+ kp2.symbol_name = probe_lists[j].api;
+ kp2.offset = probe_lists[j].offset;
+ kp2.pre_handler = kp2_pre_handler;
+ kp2.post_handler = kp2_post_handler;
+ kp2.fault_handler = kp_fault_handler;
+ kp2_index = probe_lists[j].reg;
+ kp2_value = probe_lists[j].value;
+ }
+ }
+ }
+ }
+
+ symbol_addr = (kprobe_opcode_t *)kallsyms_lookup_name(kp.symbol_name);
+ if (!symbol_addr) {
+ printk(KERN_INFO "Could not find address of %s\n", kp.symbol_name);
+ return -1;
+ }
+ printk(KERN_INFO "kp1.symbol_addr = 0x%lx\n", symbol_addr);
+
+ if (symbol_num == 1) {
+ ret = register_kprobe(&kp);
+ if (ret < 0) {
+ printk(KERN_INFO "register_kprobe failed, returned %d\n", ret);
+ return ret;
+ }
+ printk(KERN_INFO "Planted kprobe1: %s+%pK at %pK\n", kp.symbol_name, (kprobe_opcode_t *)kp.offset, (kprobe_opcode_t *)kp.addr);
+ }
+ else if (symbol_num == 2) {
+ ret = register_kprobes(kps, 2);
+ if (ret < 0) {
+ printk(KERN_INFO "register_kprobe failed, returned %d\n", ret);
+ return ret;
+ }
+ printk(KERN_INFO "Planted kprobe1: %s+%pK at %pK; kprobe2: %s+%pK at %pK\n", kp.symbol_name, (kprobe_opcode_t *)kp.offset,
+ (kprobe_opcode_t *)kp.addr, kp2.symbol_name, (kprobe_opcode_t *)kp2.offset, (kprobe_opcode_t *)kp2.addr);
+ }
+
+ return 0;
+}
+
+static void __exit kprobe_exit(void)
+{
+ if (symbol_num == 1) {
+ unregister_kprobe(&kp);
+ printk(KERN_INFO "kprobe at %pK unregistered\n", kp.addr);
+ }
+ else {
+ unregister_kprobes(kps, 2);
+ printk(KERN_INFO "kprobe1 at %pK and kprobe2 at %pK unregistered\n", kp.addr, kp2.addr);
+ }
+}
+
+module_init(kprobe_init)
+module_exit(kprobe_exit)
+MODULE_LICENSE("GPL v2");
diff --git a/virtio_clk/run_fault_injection.sh b/virtio_clk/run_fault_injection.sh
new file mode 100644
index 0000000..86e08d3
--- /dev/null
+++ b/virtio_clk/run_fault_injection.sh
@@ -0,0 +1,102 @@
+#!/bin/bash
+#
+# Copyright (c) 2019, The Linux Foundation. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 and
+# only 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.
+
+symbol_1="of_clk_hw_virtio_get"
+symbol_2="virtio_clk_prepare"
+symbol_3="virtio_clk_set_rate"
+symbol_4="virtio_clk_get_rate"
+symbol_5="virtio_clk_round_rate"
+symbol_6="virtio_clk_set_flags"
+symbol_7="__virtio_reset"
+
+offset_1=(0x134 0x6c)
+offset_2=(0x920 0xa14 0xab4)
+offset_3=(0xcc4 0xdb8 0xe58)
+offset_4=(0x934 0xa28 0xac4 0xb50)
+offset_5=(0xcc0 0xdb4 0xe50 0xedc)
+offset_6=(0xcb0 0xda4 0xe40)
+offset_7=(0xd68 0xe48 0xed4)
+
+insmod fault_injection_virtio_clk_test.ko
+
+# Fault injection for of_clk_hw_virtio_get
+for i in ${offset_1[*]}
+do
+ echo "Run test case:Inject_clk_get symbol:$symbol_1 offset:$i"
+ insmod kprobe_unit_test.ko symbol=$symbol_1 offset=$i
+ ktfrun --gtest_color=yes --gtest_filter=fault_injection_1.*
+ sleep 5
+ rmmod kprobe_unit_test.ko
+done
+
+# Fault injection for virtio_clk_prepare
+for i in ${offset_2[*]}
+do
+ echo "Run test case:Inject_clk_prepare symbol:$symbol_2 offset:$i"
+ insmod kprobe_unit_test.ko symbol=$symbol_2 offset=$i
+ ktfrun --gtest_color=yes --gtest_filter=fault_injection_2.*
+ sleep 5
+ rmmod kprobe_unit_test.ko
+done
+
+# Fault injection for virtio_clk_set_rate
+for i in ${offset_3[*]}
+do
+ echo "Run test case:Inject_clk_set_rate symbol:$symbol_3 offset:$i"
+ insmod kprobe_unit_test.ko symbol=$symbol_3 offset=$i
+ ktfrun --gtest_color=yes --gtest_filter=fault_injection_3.*
+ sleep 5
+ rmmod kprobe_unit_test.ko
+done
+
+# Fault injection for virtio_clk_get_rate
+for i in ${offset_4[*]}
+do
+ echo "Run test case:Inject_clk_get_rate symbol:$symbol_4 offset:$i"
+ insmod kprobe_unit_test.ko symbol=$symbol_4 offset=$i
+ ktfrun --gtest_color=yes --gtest_filter=fault_injection_4.*
+ sleep 5
+ rmmod kprobe_unit_test.ko
+done
+
+# Fault injection for virtio_clk_round_rate
+for i in ${offset_5[*]}
+do
+ echo "Run test case:Inject_clk_prepare symbol:$symbol_5 offset:$i"
+ insmod kprobe_unit_test.ko symbol=$symbol_5 offset=$i
+ ktfrun --gtest_color=yes --gtest_filter=fault_injection_5.*
+ sleep 5
+ rmmod kprobe_unit_test.ko
+done
+
+# Fault injection for virtio_clk_set_flags
+for i in ${offset_6[*]}
+do
+ echo "Run test case:Inject_clk_prepare symbol:$symbol_6 offset:$i"
+ insmod kprobe_unit_test.ko symbol=$symbol_6 offset=$i
+ ktfrun --gtest_color=yes --gtest_filter=fault_injection_6.*
+ sleep 5
+ rmmod kprobe_unit_test.ko
+done
+
+# Fault injection for __virtio_reset
+for i in ${offset_7[*]}
+do
+ echo "Run test case:Inject_clk_prepare symbol:$symbol_7 offset:$i"
+ insmod kprobe_unit_test.ko symbol=$symbol_7 offset=$i
+ ktfrun --gtest_color=yes --gtest_filter=fault_injection_7.*
+ sleep 5
+ rmmod kprobe_unit_test.ko
+done
+
+rmmod fault_injection_virtio_clk_test.ko
diff --git a/virtio_clk/virtio_clk_unit_test.c b/virtio_clk/virtio_clk_unit_test.c
new file mode 100644
index 0000000..116d40d
--- /dev/null
+++ b/virtio_clk/virtio_clk_unit_test.c
@@ -0,0 +1,521 @@
+/* Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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 <linux/clk.h>
+#include <linux/clk/qcom.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/kernel.h>
+#include <linux/reset.h>
+#include <linux/ktf.h>
+
+KTF_INIT();
+
+#define DRV_NAME "virt_clk_test"
+#define MAX_CLK 52
+#define MAX_RESET 2
+
+static struct device *dev_ut;
+static struct clk *clk_ut;
+static struct reset_control *rst_ut;
+
+typedef struct virtio_clk_ctx {
+ const char *clk_consumer_id[MAX_CLK];
+ const char *reset_controller_consumer_id[MAX_RESET];
+ char *invalide_consumer_id[5];
+ unsigned long rate_value[10];
+ long rate_outliers[3];
+}virtio_clk_ctx;
+
+static struct virtio_clk_ctx clk_test_ctx = {
+ .clk_consumer_id = {
+ "unit_test_clk_1",
+ "unit_test_clk_2",
+ "unit_test_clk_3",
+ "unit_test_clk_4",
+ "unit_test_clk_5",
+ "unit_test_clk_6",
+ "unit_test_clk_7",
+ "unit_test_clk_8",
+ "unit_test_clk_9",
+ "unit_test_clk_10",
+ "unit_test_clk_11",
+ "unit_test_clk_12",
+ "unit_test_clk_13",
+ "unit_test_clk_14",
+ "unit_test_clk_15",
+ "unit_test_clk_16",
+ "unit_test_clk_17",
+ "unit_test_clk_18",
+ "unit_test_clk_19",
+ "unit_test_clk_20",
+ "unit_test_clk_21",
+ "unit_test_clk_22",
+ "unit_test_clk_23",
+ "unit_test_clk_24",
+ "unit_test_clk_25",
+ "unit_test_clk_26",
+ "unit_test_clk_27",
+ "unit_test_clk_28",
+ "unit_test_clk_29",
+ "unit_test_clk_30",
+ "unit_test_clk_31",
+ "unit_test_clk_32",
+ "unit_test_clk_33",
+ "unit_test_clk_34",
+ "unit_test_clk_35",
+ "unit_test_clk_36",
+ "unit_test_clk_37",
+ "unit_test_clk_38",
+ "unit_test_clk_39",
+ "unit_test_clk_40",
+ "unit_test_clk_41",
+ "unit_test_clk_42",
+ "unit_test_clk_43",
+ "unit_test_clk_44",
+ "unit_test_clk_45",
+ "unit_test_clk_46",
+ "unit_test_clk_47",
+ "unit_test_clk_48",
+ "unit_test_clk_49",
+ "unit_test_clk_50",
+ "unit_test_clk_51",
+ "unit_test_clk_52",
+ },
+ .reset_controller_consumer_id = {
+ "unit_test_reset_controller_1",
+ "unit_test_reset_controller_7",
+ },
+ .invalide_consumer_id = { "gcc_gpu_cfg_ahb_clk", "mmssnoc_axi_clk", "apb_pclk", "\028", (char*) 0 },
+ .rate_value = { 0, 400000, 1200000, 19200000, 27000000, 48000000,
+ 108000000, 200000000, 600000000, ULONG_MAX},
+ .rate_outliers = { -1, -200000000, ULONG_MAX+1 },
+};
+
+enum invalid_clk_flags {
+ CLKFLAG_TESTING = 6,
+ CLKFLAG_NON_EXISTENT,
+};
+
+TEST(clock_handler, Test_clk_get)
+{
+ int i;
+
+ /*Positive testing*/
+ for (i = 0; i < MAX_CLK; i++) {
+ tlog(T_INFO, "Clock name[%d] = %s\n", i, clk_test_ctx.clk_consumer_id[i]);
+ clk_ut = clk_get(dev_ut, clk_test_ctx.clk_consumer_id[i]);
+ tlog(T_INFO, "Clock Addr[%d] = 0x%x\n", i, clk_ut);
+ ASSERT_OK_ADDR_GOTO(clk_ut, err);
+ clk_put(clk_ut);
+ }
+
+ /*Negative testing*/
+ clk_ut = clk_get(NULL, "unit_test_clk_1");
+ EXPECT_INT_LE(PTR_ERR(clk_ut), 0);
+
+ for (i = 0; i < 5; i++) {
+ clk_ut = clk_get(dev_ut, clk_test_ctx.invalide_consumer_id[i]);
+ EXPECT_INT_LT(PTR_ERR(clk_ut), 0);
+ }
+
+ return;
+
+err:
+ terr("Can't get clock %s\n", clk_test_ctx.clk_consumer_id[i]);
+}
+
+TEST(clock_handler, Test_clk_prepare_unprepare)
+{
+ int i;
+ int ret = 0;
+
+ /*Positive testing*/
+ for (i = 0; i < MAX_CLK; i++) {
+ clk_ut = clk_get(dev_ut, clk_test_ctx.clk_consumer_id[i]);
+ if (!IS_ERR_OR_NULL(clk_ut)) {
+ ret = clk_prepare(clk_ut);
+ tlog(T_INFO, "clk_prepare[%d] return %d\n", i, ret);
+ EXPECT_INT_EQ(0, ret);
+ ret = clk_enable(clk_ut);
+ tlog(T_INFO, "clk_enable[%d] return %d\n", i, ret);
+ EXPECT_INT_EQ(0, ret);
+ clk_disable(clk_ut);
+ clk_unprepare(clk_ut);
+ clk_put(clk_ut);
+ } else {
+ tlog(T_INFO, "Clock %s get failed\n", clk_test_ctx.clk_consumer_id[i]);
+ }
+ }
+
+ /*Negative testing*/
+ ret = clk_prepare(NULL);
+ EXPECT_INT_LE(ret, 0);
+}
+
+TEST(clock_handler, Test_clk_set_get_rate)
+{
+ int i,j;
+ int ret = 0;
+ unsigned long get_rate;
+
+ char *support_rate_set_clk[] = {
+ "unit_test_clk_1",
+ "unit_test_clk_2",
+ "unit_test_clk_3",
+ "unit_test_clk_4",
+ "unit_test_clk_5",
+ "unit_test_clk_6",
+ "unit_test_clk_7",
+ "unit_test_clk_8",
+ "unit_test_clk_9",
+ "unit_test_clk_10",
+ "unit_test_clk_11",
+ "unit_test_clk_12",
+ "unit_test_clk_13",
+ "unit_test_clk_14",
+ "unit_test_clk_15",
+ "unit_test_clk_16",
+ "unit_test_clk_17",
+ "unit_test_clk_18",
+ "unit_test_clk_19",
+ "unit_test_clk_20",
+ "unit_test_clk_27",
+ "unit_test_clk_33",
+ "unit_test_clk_37",
+ "unit_test_clk_42"
+ };
+ /*Positive testing*/
+ for (i = 0; i < ARRAY_SIZE(support_rate_set_clk); i++) {
+ clk_ut = clk_get(dev_ut, support_rate_set_clk[i]);
+ if (!IS_ERR_OR_NULL(clk_ut)) {
+ for (j = 0; j < 10; j++) {
+ tlog(T_INFO, "Clock name[%d] = %s, set rate = %lu\n", i, support_rate_set_clk[i], clk_test_ctx.rate_value[j]);
+ ret = clk_set_rate(clk_ut, clk_test_ctx.rate_value[j]);
+ ASSERT_INT_EQ_GOTO(0, ret, err);
+ get_rate = clk_get_rate(clk_ut);
+ EXPECT_LONG_GE(get_rate, 0);
+ }
+ clk_put(clk_ut);
+ } else {
+ tlog(T_INFO, "Clock %s get failed\n", clk_test_ctx.clk_consumer_id[i]);
+ }
+ }
+
+ /*Negative testing*/
+ for (i = 0; i < ARRAY_SIZE(support_rate_set_clk); i++) {
+ clk_ut = clk_get(dev_ut, support_rate_set_clk[i]);
+ if (!IS_ERR_OR_NULL(clk_ut)) {
+ for (j = 0; j < 3; j++) {
+ EXPECT_INT_EQ(clk_set_rate(clk_ut, clk_test_ctx.rate_outliers[j]), 0);
+ }
+ clk_put(clk_ut);
+ } else {
+ tlog(T_INFO, "Clock %s get failed\n", clk_test_ctx.clk_consumer_id[i]);
+ }
+ }
+
+ EXPECT_INT_LE(clk_set_rate(NULL, 1200000), 0);
+ EXPECT_LONG_LE(clk_get_rate(NULL), 0);
+
+ return;
+
+err:
+ terr("Clock %s set rate %lu failed\n", clk_test_ctx.clk_consumer_id[i], clk_test_ctx.rate_value[j]);
+ clk_put(clk_ut);
+}
+
+TEST(clock_handler, Test_clk_round_rate)
+{
+ int i;
+ long round_rate, round_invalid_rate, lowest_available, round_rate_step;
+ /*Positive testing*/
+ clk_ut = clk_get(dev_ut, "unit_test_clk_1");
+ if (!IS_ERR_OR_NULL(clk_ut)) {
+ lowest_available = clk_round_rate(clk_ut, 0);
+ ASSERT_INT_GT_GOTO(lowest_available, 0, err);
+ round_rate = clk_round_rate(clk_ut, 1200000);
+ ASSERT_INT_GT_GOTO(round_rate, 0, err);
+ round_rate_step = clk_round_rate(clk_ut, 27000000);
+ ASSERT_INT_GT_GOTO(round_rate_step, 0, err);
+ tlog(T_INFO, "Clock unit_test_clk_1 round rate 1200000 return %ld, 27000000 return %ld, lowest_available rate is %ld\n", round_rate, round_rate_step, lowest_available);
+ EXPECT_LONG_GE(round_rate, lowest_available);
+ EXPECT_LONG_GE(round_rate_step, round_rate);
+ } else {
+ tlog(T_INFO, "Clock unit_test_clk_1 get failed\n");
+ }
+ clk_put(clk_ut);
+
+ /*Negative testing*/
+ clk_ut = clk_get(dev_ut, "unit_test_clk_1");
+ if (!IS_ERR_OR_NULL(clk_ut)) {
+ for (i = 0; i < 3; i++) {
+ tlog(T_INFO, "Clock unit_test_clk_1 round invalide rate %ld\n", clk_test_ctx.rate_outliers[i]);
+ round_invalid_rate = clk_round_rate(clk_ut, clk_test_ctx.rate_outliers[i]);
+ EXPECT_LONG_GE(round_invalid_rate, 0);
+ }
+ } else {
+ tlog(T_INFO, "Clock unit_test_clk_1 get failed\n");
+ }
+ clk_put(clk_ut);
+ round_rate = clk_round_rate(NULL, 1200000);
+ EXPECT_LONG_LE(round_rate, 0);
+
+ return;
+
+err:
+ terr("Unable to round clk\n");
+ clk_put(clk_ut);
+}
+
+TEST(clock_handler, Test_clk_set_flags)
+{
+ int i, ret;
+ char *flags_clk[] = {
+ "unit_test_clk_45",
+ "unit_test_clk_46",
+ };
+ /*Positive testing*/
+ for (i = 0; i < ARRAY_SIZE(flags_clk); i++) {
+ tlog(T_INFO, "Clock name[%d] = %s", i, flags_clk[i]);
+ clk_ut = clk_get(dev_ut, flags_clk[i]);
+ if (!IS_ERR_OR_NULL(clk_ut)) {
+ EXPECT_INT_EQ(-1, clk_set_flags(clk_ut, CLKFLAG_NORETAIN_MEM));
+ EXPECT_INT_EQ(-1, clk_set_flags(clk_ut, CLKFLAG_NORETAIN_PERIPH));
+ clk_put(clk_ut);
+ } else {
+ tlog(T_INFO, "Clock %s get failed\n", clk_test_ctx.clk_consumer_id[i]);
+ }
+ }
+
+ /*Negative testing*/
+ for (i = 0; i < MAX_CLK; i++) {
+ clk_ut = clk_get(dev_ut, clk_test_ctx.clk_consumer_id[i]);
+ if (!IS_ERR_OR_NULL(clk_ut)) {
+ EXPECT_INT_LT(clk_set_flags(clk_ut, CLKFLAG_TESTING), 0);
+ EXPECT_INT_LT(clk_set_flags(clk_ut, CLKFLAG_NON_EXISTENT), 0);
+ clk_put(clk_ut);
+ } else {
+ tlog(T_INFO, "Clock %s get failed\n", clk_test_ctx.clk_consumer_id[i]);
+ }
+ }
+
+ ret = clk_set_flags(NULL, CLKFLAG_RETAIN_PERIPH);
+ EXPECT_INT_LE(ret, 0);
+
+}
+
+TEST(clock_reset, Test_reset_control_get)
+{
+ int i;
+ /*Positive testing*/
+ for (i = 0; i < MAX_RESET; i++) {
+ tlog(T_INFO, "Reset controller [%d] = %s\n", i, clk_test_ctx.reset_controller_consumer_id[i]);
+ rst_ut = reset_control_get(dev_ut, clk_test_ctx.reset_controller_consumer_id[i]);
+ tlog(T_INFO, "Addr = 0x%x\n", rst_ut);
+ ASSERT_OK_ADDR_GOTO(rst_ut, err);
+ reset_control_put(rst_ut);
+ }
+
+ /*Negative testing*/
+ for (i = 0; i < 5; i++) {
+ rst_ut = reset_control_get(dev_ut, clk_test_ctx.invalide_consumer_id[i]);
+ EXPECT_INT_LT(PTR_ERR(rst_ut), 0);
+ }
+ return;
+
+err:
+ terr("Can't get reset %s\n", clk_test_ctx.reset_controller_consumer_id[i]);
+}
+
+TEST(clock_reset, Test_reset_control_reset)
+{
+ int i;
+ int ret = 0;
+ /*Positive testing*/
+ for (i = 0; i < MAX_RESET; i++) {
+ rst_ut = reset_control_get(dev_ut, clk_test_ctx.reset_controller_consumer_id[i]);
+ if (!IS_ERR_OR_NULL(rst_ut)) {
+ ret = reset_control_reset(rst_ut);
+ EXPECT_INT_EQ(0, ret);
+ reset_control_put(rst_ut);
+ } else {
+ tlog(T_INFO, "reset controller %s get failed\n", clk_test_ctx.reset_controller_consumer_id[i]);
+ }
+ }
+
+ /*Negative testing*/
+ ret = reset_control_reset(NULL);
+ EXPECT_INT_LE(ret, 0);
+}
+
+TEST(clock_reset, Test_reset_control_assert)
+{
+ int i;
+ int ret = 0;
+ /*Positive testing*/
+ for (i = 0; i < MAX_RESET; i++) {
+ rst_ut = reset_control_get(dev_ut, clk_test_ctx.reset_controller_consumer_id[i]);
+ if (!IS_ERR_OR_NULL(rst_ut)) {
+ ret = reset_control_assert(rst_ut);
+ ASSERT_INT_EQ_GOTO(0, ret, err);
+ reset_control_deassert(rst_ut);
+ reset_control_put(rst_ut);
+ }
+ }
+
+ /*Negative testing*/
+ ret = reset_control_assert(NULL);
+ EXPECT_INT_LE(ret, 0);
+
+ return;
+
+err:
+ terr("Fail to assert %s reset\n", clk_test_ctx.reset_controller_consumer_id[i]);
+ reset_control_put(rst_ut);
+}
+
+TEST(clock_reset, Test_reset_control_deassert)
+{
+ int i;
+ int ret = 0;
+
+ /*Positive testing*/
+ for (i = 0; i < MAX_RESET; i++) {
+ rst_ut = reset_control_get(dev_ut, clk_test_ctx.reset_controller_consumer_id[i]);
+ if (!IS_ERR_OR_NULL(rst_ut)) {
+ ret = reset_control_assert(rst_ut);
+ if (!ret) {
+ ret = reset_control_deassert(rst_ut);
+ ASSERT_INT_EQ_GOTO(0, ret, err);
+ reset_control_put(rst_ut);
+ }
+ }
+ }
+
+ /*Negative testing*/
+ ret = reset_control_assert(NULL);
+ EXPECT_INT_LE(ret, 0);
+
+ return;
+
+err:
+ terr("Fail to deassert %s reset\n", clk_test_ctx.reset_controller_consumer_id[i]);
+ reset_control_put(rst_ut);
+}
+
+TEST(clock_reset, Test_reset_control_status)
+{
+ int i;
+ int ret = 0;
+
+ /*Positive testing*/
+ rst_ut = reset_control_get(dev_ut, "unit_test_reset_controller_1");
+ if (!IS_ERR_OR_NULL(rst_ut)) {
+ if(!reset_control_assert(rst_ut)) {
+ ret = reset_control_status(rst_ut);
+ ASSERT_INT_GT_GOTO(ret, 0, err);
+ ret = reset_control_deassert(rst_ut);
+ if (!ret) {
+ EXPECT_INT_EQ(0, reset_control_status(rst_ut));
+ reset_control_put(rst_ut);
+ }
+ }
+ }
+
+ /*Negative testing*/
+ ret = reset_control_status(NULL);
+ EXPECT_INT_LE(ret, 0);
+
+ return;
+
+err:
+ terr("Fail to query %s reset status\n", clk_test_ctx.reset_controller_consumer_id[i]);
+ reset_control_put(rst_ut);
+}
+
+
+static const struct of_device_id virt_clk_test_dt_ids[] = {
+ { .compatible = "qcom,virtio-clock-unit-test" },
+ {}
+};
+
+static int virt_clk_test_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *id;
+ tlog(T_INFO, "virt_clk_test_probe\n");
+ id = of_match_device(virt_clk_test_dt_ids, &pdev->dev);
+ if (!id) {
+ terr("No matching device found\n");
+ return -ENODEV;
+ }
+ tlog(T_INFO, "of_match_device, &pdev->dev = 0x%x\n", &pdev->dev);
+
+ dev_ut = &pdev->dev;
+
+ return 0;
+}
+
+static int virt_clk_test_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static void add_tests(void)
+{
+ ADD_TEST(Test_clk_get);
+ ADD_TEST(Test_clk_prepare_unprepare);
+ ADD_TEST(Test_clk_set_get_rate);
+ ADD_TEST(Test_clk_round_rate);
+ ADD_TEST(Test_clk_set_flags);
+ ADD_TEST(Test_reset_control_get);
+ ADD_TEST(Test_reset_control_reset);
+ ADD_TEST(Test_reset_control_assert);
+ ADD_TEST(Test_reset_control_deassert);
+ ADD_TEST(Test_reset_control_status);
+}
+
+static struct platform_driver virt_clk_test_driver = {
+ .probe = virt_clk_test_probe,
+ .remove = virt_clk_test_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = virt_clk_test_dt_ids,
+ },
+};
+
+static int __init virtio_clk_ut_init(void)
+{
+ int ret;
+ tlog(T_INFO, "Unit test kernel module for virtio clock initialising\n");
+
+ add_tests();
+
+ ret = platform_driver_register(&virt_clk_test_driver);
+ if (ret)
+ terr("Error registering platform driver\n");
+
+ tlog(T_INFO, "Driver initialized\n");
+ return ret;
+}
+
+static void __exit virtio_clk_ut_exit(void)
+{
+ platform_driver_unregister(&virt_clk_test_driver);
+ tlog(T_INFO, "virtio_clk unit test kernel module Unloaded\n");
+ KTF_CLEANUP();
+}
+
+module_init(virtio_clk_ut_init);
+module_exit(virtio_clk_ut_exit);
+MODULE_LICENSE("GPL v2");