linux-spi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH -next 00/14] Implement a ligth weight device driver test framework
@ 2023-11-18 10:40 Zhang Xiaoxu
  2023-11-18 10:40 ` [PATCH -next 01/14] kddv/core: " Zhang Xiaoxu
                   ` (13 more replies)
  0 siblings, 14 replies; 15+ messages in thread
From: Zhang Xiaoxu @ 2023-11-18 10:40 UTC (permalink / raw)
  To: zhangxiaoxu5, weiyongjun1, linux-kernel, broonie, rostedt, mingo,
	frowand.list, linux-spi

From: Zhang Xiaoxu <zhangxiaoxu@huawei.com>

Implement a ligth weight device driver test framework, KDDV(Kernel
Device Driver Verification). Which using eBPF based bus controllers
to mockup device chipsets.

The kddv already discover the following issues:
  fc92d9e3de0b iio: health: afe4404: Fix oob read in afe4404_[read|write]_raw
  58143c1ed588 iio: health: afe4403: Fix oob read in afe4403_read_raw
  3f4033a811bc iio: filter: admv8818: close potential out-of-bounds read in __admv8818_read_[h|l]pf_freq()
  8c9a59939deb Input: raydium_ts_i2c - fix memory leak in raydium_i2c_send()
  7485edb2b6ca media: i2c: ov772x: Fix memleak in ov772x_probe()
  bab76514aca3 regulator: da9121: Fix uninit-value in da9121_assign_chip_model()
  9d47e01b9d80 power: supply: adp5061: fix out-of-bounds read in adp5061_get_chg_type()
  ...

Zhang Xiaoxu (14):
  kddv/core: Implement a ligth weight device driver test framework
  kddv/core: Allow test case config bpf program
  kddv/core: Add io fault support to bpf program
  kddv/core: Check kmsg before return from test case
  kddv/core: Support kernel memory leak detector
  kddv/core: Add page and slab fault inject support
  kddv/cmd: Add command to create/remove mockup device
  kddv/cmd: Add command to run testcases
  kddv/core: Add test support for SPI driver
  kddv/tests: Add support for testing hwmon driver
  kddv/tests/hwmon: Add test cases for max31722 driver
  kddv/tests: Add support for testing mtd driver
  kddv/tests/mtd: Add test cases for mchp23k256 driver
  kddv: Add document for kddv

 Documentation/dev-tools/index.rst             |   1 +
 Documentation/dev-tools/kddv.rst              | 183 ++++++++
 tools/testing/kddv/.gitignore                 |   3 +
 tools/testing/kddv/Makefile                   |  25 ++
 tools/testing/kddv/kddv/Makefile              |  28 ++
 tools/testing/kddv/kddv/__init__.py           |   0
 tools/testing/kddv/kddv/cmds/__init__.py      |   0
 tools/testing/kddv/kddv/cmds/mock.py          | 105 +++++
 tools/testing/kddv/kddv/cmds/test.py          |  75 ++++
 tools/testing/kddv/kddv/cmds/utils.py         |  28 ++
 tools/testing/kddv/kddv/core/__init__.py      |  13 +
 .../testing/kddv/kddv/core/buses/__init__.py  |  13 +
 tools/testing/kddv/kddv/core/buses/spi.py     |  74 +++
 tools/testing/kddv/kddv/core/consts.py        |  13 +
 tools/testing/kddv/kddv/core/ddunit.py        |  83 ++++
 tools/testing/kddv/kddv/core/device.py        |  78 ++++
 tools/testing/kddv/kddv/core/dmesg.py         |  41 ++
 tools/testing/kddv/kddv/core/driver.py        |  82 ++++
 tools/testing/kddv/kddv/core/environ.py       |  65 +++
 tools/testing/kddv/kddv/core/failnth.py       |  57 +++
 tools/testing/kddv/kddv/core/faulter.py       |  48 ++
 .../testing/kddv/kddv/core/faults/__init__.py |  13 +
 tools/testing/kddv/kddv/core/faults/fail.py   |  86 ++++
 tools/testing/kddv/kddv/core/faults/page.py   |  40 ++
 tools/testing/kddv/kddv/core/faults/slab.py   |  36 ++
 tools/testing/kddv/kddv/core/memleak.py       |  39 ++
 tools/testing/kddv/kddv/core/mockup.py        | 193 ++++++++
 tools/testing/kddv/kddv/core/model.py         |  95 ++++
 tools/testing/kddv/kddv/data/Makefile         |  21 +
 tools/testing/kddv/kddv/data/bpf/Makefile     |  22 +
 .../kddv/data/bpf/include/bpf-xfer-conf.h     | 124 +++++
 .../kddv/kddv/data/bpf/mtd/mtd-mchp23k256.c   |  72 +++
 .../kddv/kddv/data/bpf/spi/spi-xfer-base.h    |  99 ++++
 .../kddv/kddv/data/bpf/spi/spi-xfer-r1v1.c    |  51 +++
 .../kddv/kddv/data/bpf/spi/spi-xfer-r1v2.c    |  51 +++
 .../kddv/kddv/data/bpf/spi/spi-xfer-r1v3.c    |  86 ++++
 .../kddv/kddv/data/bpf/spi/spi-xfer-r2v1.c    |  51 +++
 .../kddv/kddv/data/bpf/spi/spi-xfer-r2v2.c    |  51 +++
 .../kddv/kddv/data/bpf/spi/spi-xfer-r3v1.c    |  52 +++
 .../kddv/kddv/data/bpf/spi/spi-xfer-r4v4.c    |  89 ++++
 tools/testing/kddv/kddv/tests/__init__.py     |   0
 .../testing/kddv/kddv/tests/hwmon/__init__.py | 425 ++++++++++++++++++
 .../kddv/kddv/tests/hwmon/test_max31722.py    |  40 ++
 tools/testing/kddv/kddv/tests/mtd/__init__.py |  63 +++
 .../kddv/kddv/tests/mtd/test_mchp23k256.py    |  41 ++
 45 files changed, 2855 insertions(+)
 create mode 100755 Documentation/dev-tools/kddv.rst
 create mode 100644 tools/testing/kddv/.gitignore
 create mode 100644 tools/testing/kddv/Makefile
 create mode 100644 tools/testing/kddv/kddv/Makefile
 create mode 100755 tools/testing/kddv/kddv/__init__.py
 create mode 100755 tools/testing/kddv/kddv/cmds/__init__.py
 create mode 100755 tools/testing/kddv/kddv/cmds/mock.py
 create mode 100755 tools/testing/kddv/kddv/cmds/test.py
 create mode 100755 tools/testing/kddv/kddv/cmds/utils.py
 create mode 100755 tools/testing/kddv/kddv/core/__init__.py
 create mode 100755 tools/testing/kddv/kddv/core/buses/__init__.py
 create mode 100755 tools/testing/kddv/kddv/core/buses/spi.py
 create mode 100755 tools/testing/kddv/kddv/core/consts.py
 create mode 100755 tools/testing/kddv/kddv/core/ddunit.py
 create mode 100755 tools/testing/kddv/kddv/core/device.py
 create mode 100755 tools/testing/kddv/kddv/core/dmesg.py
 create mode 100755 tools/testing/kddv/kddv/core/driver.py
 create mode 100755 tools/testing/kddv/kddv/core/environ.py
 create mode 100755 tools/testing/kddv/kddv/core/failnth.py
 create mode 100755 tools/testing/kddv/kddv/core/faulter.py
 create mode 100755 tools/testing/kddv/kddv/core/faults/__init__.py
 create mode 100755 tools/testing/kddv/kddv/core/faults/fail.py
 create mode 100755 tools/testing/kddv/kddv/core/faults/page.py
 create mode 100755 tools/testing/kddv/kddv/core/faults/slab.py
 create mode 100755 tools/testing/kddv/kddv/core/memleak.py
 create mode 100755 tools/testing/kddv/kddv/core/mockup.py
 create mode 100755 tools/testing/kddv/kddv/core/model.py
 create mode 100644 tools/testing/kddv/kddv/data/Makefile
 create mode 100644 tools/testing/kddv/kddv/data/bpf/Makefile
 create mode 100644 tools/testing/kddv/kddv/data/bpf/include/bpf-xfer-conf.h
 create mode 100644 tools/testing/kddv/kddv/data/bpf/mtd/mtd-mchp23k256.c
 create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-base.h
 create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r1v1.c
 create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r1v2.c
 create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r1v3.c
 create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r2v1.c
 create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r2v2.c
 create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r3v1.c
 create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r4v4.c
 create mode 100755 tools/testing/kddv/kddv/tests/__init__.py
 create mode 100755 tools/testing/kddv/kddv/tests/hwmon/__init__.py
 create mode 100755 tools/testing/kddv/kddv/tests/hwmon/test_max31722.py
 create mode 100644 tools/testing/kddv/kddv/tests/mtd/__init__.py
 create mode 100755 tools/testing/kddv/kddv/tests/mtd/test_mchp23k256.py

-- 
2.34.1


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH -next 01/14] kddv/core: Implement a ligth weight device driver test framework
  2023-11-18 10:40 [PATCH -next 00/14] Implement a ligth weight device driver test framework Zhang Xiaoxu
@ 2023-11-18 10:40 ` Zhang Xiaoxu
  2023-11-18 10:40 ` [PATCH -next 02/14] kddv/core: Allow test case config bpf program Zhang Xiaoxu
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Zhang Xiaoxu @ 2023-11-18 10:40 UTC (permalink / raw)
  To: zhangxiaoxu5, weiyongjun1, linux-kernel, broonie, rostedt, mingo,
	frowand.list, linux-spi

From: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>

Implement a ligth weight device driver test framework, KDDV(Kernel
Device Driver Verification). Which using eBPF based bus controllers
to mockup device chipsets.

Directory structure as below:

  $ tree tools/testing/kddv/kddv
  tools/testing/kddv/kddv/
  ├── core           # general data structure of the kddv
  │   ├── __init__.py
  │   ├── ddunit.py  # basic testcase class
  │   ├── device.py  # device common info and operations
  │   ├── driver.py  # driver controller, load modules and bind device
  │   ├── mockup.py  # bpf controller, mockup hardware
  │   └── model.py   # drivers common info and operations
  ├── __init__.py
  ├── Makefile
  └── tests             # test case folder
      └── __init__.py

Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>
---
 tools/testing/kddv/.gitignore             |   3 +
 tools/testing/kddv/Makefile               |  25 ++++
 tools/testing/kddv/kddv/Makefile          |  22 +++
 tools/testing/kddv/kddv/__init__.py       |   0
 tools/testing/kddv/kddv/core/__init__.py  |   7 +
 tools/testing/kddv/kddv/core/ddunit.py    |  44 ++++++
 tools/testing/kddv/kddv/core/device.py    |  78 +++++++++++
 tools/testing/kddv/kddv/core/driver.py    |  79 +++++++++++
 tools/testing/kddv/kddv/core/mockup.py    | 157 ++++++++++++++++++++++
 tools/testing/kddv/kddv/core/model.py     |  87 ++++++++++++
 tools/testing/kddv/kddv/tests/__init__.py |   0
 11 files changed, 502 insertions(+)
 create mode 100644 tools/testing/kddv/.gitignore
 create mode 100644 tools/testing/kddv/Makefile
 create mode 100644 tools/testing/kddv/kddv/Makefile
 create mode 100755 tools/testing/kddv/kddv/__init__.py
 create mode 100755 tools/testing/kddv/kddv/core/__init__.py
 create mode 100755 tools/testing/kddv/kddv/core/ddunit.py
 create mode 100755 tools/testing/kddv/kddv/core/device.py
 create mode 100755 tools/testing/kddv/kddv/core/driver.py
 create mode 100755 tools/testing/kddv/kddv/core/mockup.py
 create mode 100755 tools/testing/kddv/kddv/core/model.py
 create mode 100755 tools/testing/kddv/kddv/tests/__init__.py

diff --git a/tools/testing/kddv/.gitignore b/tools/testing/kddv/.gitignore
new file mode 100644
index 000000000000..159cec72565a
--- /dev/null
+++ b/tools/testing/kddv/.gitignore
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+__pycache__/
+*.py[cod]
\ No newline at end of file
diff --git a/tools/testing/kddv/Makefile b/tools/testing/kddv/Makefile
new file mode 100644
index 000000000000..f4ef60a1342b
--- /dev/null
+++ b/tools/testing/kddv/Makefile
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0
+include ../../scripts/Makefile.include
+
+SUBDIRS := kddv
+
+all:
+	@for SUBDIR in $(SUBDIRS); do \
+		$(MAKE) -C $$SUBDIR;\
+	done;
+
+install:
+ifdef INSTALL_PATH
+	@for SUBDIR in $(SUBDIRS); do \
+		$(MAKE) INSTALL_PATH=$(INSTALL_PATH)/$$SUBDIR -C $$SUBDIR install;\
+	done;
+else
+	$(error Error: set INSTALL_PATH to use install)
+endif
+
+clean:
+	@for SUBDIR in $(SUBDIRS); do \
+		$(MAKE) -C $$SUBDIR clean;\
+	done;
+
+.PHONY: all install clean
diff --git a/tools/testing/kddv/kddv/Makefile b/tools/testing/kddv/kddv/Makefile
new file mode 100644
index 000000000000..a68112154669
--- /dev/null
+++ b/tools/testing/kddv/kddv/Makefile
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0
+include ../../../scripts/Makefile.include
+
+INSTALL ?= install
+
+all:
+	@for SUBDIR in $(SUBDIRS); do		\
+		$(MAKE) -C $$SUBDIR;		\
+	done;
+
+install:
+	$(INSTALL) -m 0755 -d $(INSTALL_PATH)
+	$(INSTALL) __init__.py $(INSTALL_PATH)
+	cp -rf core/ $(INSTALL_PATH)
+	cp -rf tests/ $(INSTALL_PATH)
+
+clean:
+	@for SUBDIR in $(SUBDIRS); do		\
+		$(MAKE) -C $$SUBDIR clean;	\
+	done;
+
+.PHONY: all install clean
diff --git a/tools/testing/kddv/kddv/__init__.py b/tools/testing/kddv/kddv/__init__.py
new file mode 100755
index 000000000000..e69de29bb2d1
diff --git a/tools/testing/kddv/kddv/core/__init__.py b/tools/testing/kddv/kddv/core/__init__.py
new file mode 100755
index 000000000000..45a35c909e86
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/__init__.py
@@ -0,0 +1,7 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
diff --git a/tools/testing/kddv/kddv/core/ddunit.py b/tools/testing/kddv/kddv/core/ddunit.py
new file mode 100755
index 000000000000..fd4ab9fe048c
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/ddunit.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+import logging
+import unittest
+
+from .model import DriverModel
+
+logger = logging.getLogger(__name__)
+
+class DriverTest(unittest.TestCase, DriverModel):
+    def __init__(self, methodName=None):
+        super().__init__(methodName)
+        DriverModel.__init__(self)
+
+    def setUp(self):
+        super().setUp()
+        try:
+            self.driver.setup()
+        except:
+            self.skipTest(f"Module {self.module_name} not found")
+        self.mockup.load()
+
+    def tearDown(self):
+        self.mockup.unload()
+        self.driver.teardown()
+        super().tearDown()
+
+    def assertRegEqual(self, reg, data, msg=None):
+        value = self.read_reg(reg)
+        self.assertEqual(value, data, msg)
+
+    def assertRegBitsEqual(self, reg, data, mask, msg=None):
+        value = self.read_reg(reg)
+        self.assertEqual(value & mask, data & mask, msg)
+
+    def assertRegsEqual(self, reg, data, msg=None):
+        value = self.read_regs(reg, len(data))
+        self.assertListEqual(value, data, msg)
diff --git a/tools/testing/kddv/kddv/core/device.py b/tools/testing/kddv/kddv/core/device.py
new file mode 100755
index 000000000000..db862890e9a4
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/device.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+import os
+import logging
+
+from pathlib import Path
+
+logger = logging.getLogger(__name__)
+
+class Device(object):
+    bus = None
+
+    def __init__(self, bus, driver, busid, addr, devid):
+        self.bus = bus
+        self.driver = driver
+        self.busid = busid
+        self.addr = addr
+        self.devid = devid
+        self.status = False
+        self.path = Path(f"/sys/bus/{self.bus}/devices/{self.device_id}")
+
+    def __enter__(self):
+        self.bind()
+        return self
+
+    def __exit__(self, exc_type, exc_value, traceback):
+        self.unbind()
+
+    def __del__(self):
+        self.unbind()
+
+    @classmethod
+    def create(cls, bus, driver, busid, addr, devid):
+        for subclass in cls.__subclasses__():
+            if subclass.bus == bus:
+                return subclass(bus, driver, busid, addr, devid)
+        return cls(bus, driver, busid, addr, devid)
+
+    @property
+    def device_id(self):
+        return self.devid
+
+    def bind(self):
+        if self.status is True:
+            return
+        self.status = True
+        self.driver.bind(self.device_id)
+
+    def unbind(self):
+        if self.status is False:
+            return
+        try:
+            self.driver.unbind(self.device_id)
+        except:
+            pass
+        self.status = False
+
+    def read_attr(self, attr):
+        path = self.path / attr
+        if not os.path.exists(path):
+            logger.info(f"attr '{attr}' not exists")
+            return None
+        logger.debug(f"read from {path}")
+        return path.read_text().rstrip()
+
+    def write_attr(self, attr, val):
+        path = self.path / attr
+        if not os.path.exists(path):
+            logger.info(f"attr '{attr}' not exists")
+            return
+        logger.debug(f"write '{val}' to {path}")
+        return path.write_bytes(val.encode())
diff --git a/tools/testing/kddv/kddv/core/driver.py b/tools/testing/kddv/kddv/core/driver.py
new file mode 100755
index 000000000000..55ad804068b5
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/driver.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+import os
+import logging
+import subprocess
+from pathlib import Path
+
+from .device import Device
+
+logger = logging.getLogger(__name__)
+
+class Driver(object):
+    def __init__(self, p):
+        self.bus = p.bus
+        self.domain_nr = p.domain_nr
+        self.bus_id = p.bus_id
+        self.parent_bus = p.parent_bus
+        self.driver = p.driver_name
+        self.module = p.module_name
+        self.depmod = p.dependencies
+        self.path = Path(f"/sys/bus/{self.bus}/drivers/{self.driver}")
+
+    def write_ctrl(self, spath, val):
+        path = self.path / spath
+        logger.debug(f"write '{val}' to {path}")
+        return path.write_text(val)
+
+    def disable_autoprobe(self):
+        logger.debug(f"disable {self.bus} drivers autoprobe")
+        path = Path(f"/sys/bus/{self.bus}/drivers_autoprobe")
+        path.write_text("0")
+
+    def enable_autoprobe(self):
+        logger.debug(f"enable {self.bus} drivers autoprobe")
+        path = Path(f"/sys/bus/{self.bus}/drivers_autoprobe")
+        path.write_text("1")
+
+    def probe_depmod(self):
+        if not self.depmod:
+            return
+        for mod in self.depmod:
+            logger.debug(f'modprobe {mod}')
+            subprocess.check_output(["/sbin/modprobe", mod])
+
+    def probe_module(self):
+        self.probe_depmod()
+        logger.debug(f'modprobe {self.module}')
+        subprocess.check_output(
+            ["/sbin/modprobe", self.module], stderr=subprocess.STDOUT
+        )
+
+    def remove_mdule(self):
+        logger.debug(f'rmmod {self.module}')
+        subprocess.check_output(["/sbin/rmmod", self.module])
+
+    def setup(self):
+        self.disable_autoprobe()
+        self.probe_module()
+
+    def teardown(self):
+        self.remove_mdule()
+        self.enable_autoprobe()
+
+    def bind(self, devid):
+        if os.path.exists(self.path / devid):
+            return
+        self.write_ctrl("bind", devid)
+
+    def unbind(self, devid):
+        self.write_ctrl("unbind", devid)
+
+    def device(self, addr, devid):
+        return Device.create(self.bus, self, self.bus_id, addr, devid)
diff --git a/tools/testing/kddv/kddv/core/mockup.py b/tools/testing/kddv/kddv/core/mockup.py
new file mode 100755
index 000000000000..b5e6c83c9164
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/mockup.py
@@ -0,0 +1,157 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+import os
+import re
+import json
+import struct
+import logging
+import subprocess
+
+from pathlib import Path
+
+logger = logging.getLogger(__name__)
+
+ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+ROOT_BPF = os.path.join(ROOT_DIR, 'data', 'bpf')
+DEST_BPF = '/sys/fs/bpf'
+
+class Mockup(object):
+    bus = None
+
+    def __init__(self, bus, p) -> None:
+        self.bus = bus
+        self.bpf = p.bpf
+        self.addr = p.address
+        self.devid = p.device_id
+        self.regshift = p.regshift
+        self.regbytes = p.regbytes
+        self.valbytes = p.valbytes
+        self.regmaps = p.regmaps
+
+    @classmethod
+    def create(cls, bus, p):
+        for sub in cls.__subclasses__():
+            if sub.bus == bus:
+                return sub(bus, p)
+        return cls(bus, p)
+
+    @property
+    def device_id(self):
+        return f"{self.addr}"
+
+    @property
+    def bpftool(self):
+        return os.environ.get('BPFTOOL_PATH', 'bpftool')
+
+    def get_valbytes(self):
+        if self.valbytes == 3:
+            return 4
+        return self.valbytes
+
+    def search_file(self, path, filename):
+        for curfile in os.listdir(path):
+            abspath = os.path.join(path, curfile)
+            if os.path.isdir(abspath):
+                subfile = self.search_file(abspath, filename)
+                if subfile is not None:
+                    return subfile
+            if curfile == filename:
+                return abspath
+        return None
+
+    def bpf_prog_name(self):
+        bpf_name = re.sub("-", "_", os.path.basename(self.bpf))
+        return f'{bpf_name}'[:15]
+
+    def load_bpf(self):
+        if self.bpf is None:
+            return
+        bpf_file = self.search_file(ROOT_BPF, f"{self.bpf}.o")
+        if bpf_file is None:
+            logger.error(f'bpf file {self.bpf} not found')
+            return
+        logger.debug(f'load bpf {self.bpf}.o')
+        bpf_path = os.path.join(DEST_BPF, self.bpf_prog_name())
+        if os.path.exists(bpf_path):
+            os.unlink(bpf_path)
+        cmds = [self.bpftool, 'prog', 'load']
+        cmds += [bpf_file, bpf_path]
+        cmds += ['autoattach']
+        logger.debug(' '.join(cmds))
+        subprocess.check_output(cmds)
+
+    def unload_bpf(self):
+        if self.bpf is None:
+            return
+        logger.debug(f'unload bpf {self.bpf}.o')
+        bpf_path = os.path.join(DEST_BPF, self.bpf_prog_name())
+        if os.path.exists(bpf_path):
+            os.unlink(bpf_path)
+
+    def create_device(self):
+        pass
+
+    def remove_device(self):
+        pass
+
+    def load(self):
+        self.load_bpf()
+        self.load_regmaps()
+        self.create_device()
+
+    def unload(self):
+        self.remove_device()
+        self.unload_bpf()
+
+    def bpf_map_name(self):
+        bpf_name = re.sub("-", "_", self.bpf)
+        return f'regs_{bpf_name}'[:15]
+
+    def to_bpf_bytes(self, val, len):
+        return list("%d" % n for n in list(val.to_bytes(len, 'little')))
+
+    def load_regmaps(self):
+        for reg, value in self.regmaps.items():
+            if isinstance(value, list):
+                self.write_regs(reg, value)
+            else:
+                self.write_reg(reg, value)
+
+    def read_reg(self, addr):
+        if self.bpf is None:
+            return
+        cmds = [self.bpftool, 'map', 'lookup']
+        cmds += ['name', self.bpf_map_name()]
+        cmds += ['key']
+        cmds += self.to_bpf_bytes(addr, 4)
+        logger.debug(' '.join(cmds))
+        mapval = subprocess.check_output(cmds)
+        return json.loads(mapval).get("value", 0)
+
+    def read_regs(self, addr, len):
+        data = []
+        for i in range(len):
+            data += [self.read_reg(addr + (i << self.regshift))]
+        return data
+
+    def write_reg(self, addr, val):
+        if self.bpf is None:
+            return
+        cmds = [self.bpftool, 'map', 'update']
+        cmds += ['name', self.bpf_map_name()]
+        cmds += ['key']
+        cmds += self.to_bpf_bytes(addr, 4)
+        cmds += ['value']
+        cmds += self.to_bpf_bytes(val, self.get_valbytes())
+        logger.debug(' '.join(cmds))
+        subprocess.check_output(cmds)
+
+    def write_regs(self, addr, data):
+        for i in range(len(data)):
+            self.write_reg(addr + (i << self.regshift), data[i])
diff --git a/tools/testing/kddv/kddv/core/model.py b/tools/testing/kddv/kddv/core/model.py
new file mode 100755
index 000000000000..9da4716df7dc
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/model.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+from .driver import Driver
+from .mockup import Mockup
+
+class DriverModel(object):
+    bus = None
+    name = None
+    addr = 0x00
+
+    def __init__(self):
+        self.driver = Driver(self)
+        self.mockup = Mockup.create(self.bus, self)
+
+    @property
+    def driver_name(self):
+        return self.name
+
+    @property
+    def module_name(self):
+        return self.name
+
+    @property
+    def dependencies(self):
+        """List of module dependencies by running tests."""
+        return None
+
+    @property
+    def domain_nr(self):
+        return 0
+
+    @property
+    def bus_id(self):
+        return 0
+
+    @property
+    def parent_bus(self):
+        return None
+
+    @property
+    def bpf(self):
+        return None
+
+    @property
+    def address(self):
+        return self.addr
+
+    @property
+    def device_id(self):
+        return self.name
+
+    @property
+    def regshift(self):
+        return 0
+
+    @property
+    def regbytes(self):
+        return 1
+
+    @property
+    def valbytes(self):
+        return 1
+
+    @property
+    def regmaps(self):
+        return {}
+
+    def device(self):
+        return self.driver.device(self.address, self.device_id)
+
+    def read_reg(self, addr):
+        return self.mockup.read_reg(addr)
+
+    def read_regs(self, addr, len):
+        return self.mockup.read_regs(addr, len)
+
+    def write_reg(self, addr, val):
+        self.mockup.write_reg(addr, val)
+
+    def write_regs(self, addr, data):
+        self.mockup.write_regs(addr, data)
diff --git a/tools/testing/kddv/kddv/tests/__init__.py b/tools/testing/kddv/kddv/tests/__init__.py
new file mode 100755
index 000000000000..e69de29bb2d1
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH -next 02/14] kddv/core: Allow test case config bpf program
  2023-11-18 10:40 [PATCH -next 00/14] Implement a ligth weight device driver test framework Zhang Xiaoxu
  2023-11-18 10:40 ` [PATCH -next 01/14] kddv/core: " Zhang Xiaoxu
@ 2023-11-18 10:40 ` Zhang Xiaoxu
  2023-11-18 10:40 ` [PATCH -next 03/14] kddv/core: Add io fault support to " Zhang Xiaoxu
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Zhang Xiaoxu @ 2023-11-18 10:40 UTC (permalink / raw)
  To: zhangxiaoxu5, weiyongjun1, linux-kernel, broonie, rostedt, mingo,
	frowand.list, linux-spi

From: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>

Allow test case config bpf program read/write regs.

@property
def configs(self):
    return { CFG_REG_MASK: 0x3f }

CFG_REG_MASK: mask reg before use
CFG_REG_RSH: right shift reg before use
CFG_REG_LSH: left shift reg before use
CFG_REG_ORD: swap reg byteorder before use

Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>
---
 tools/testing/kddv/kddv/core/consts.py        |  12 ++
 tools/testing/kddv/kddv/core/mockup.py        |  28 +++++
 tools/testing/kddv/kddv/core/model.py         |   4 +
 .../kddv/data/bpf/include/bpf-xfer-conf.h     | 111 ++++++++++++++++++
 4 files changed, 155 insertions(+)
 create mode 100755 tools/testing/kddv/kddv/core/consts.py
 create mode 100644 tools/testing/kddv/kddv/data/bpf/include/bpf-xfer-conf.h

diff --git a/tools/testing/kddv/kddv/core/consts.py b/tools/testing/kddv/kddv/core/consts.py
new file mode 100755
index 000000000000..22abd7fc655c
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/consts.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+CFG_REG_MASK = 0x10
+CFG_REG_RSH = 0x11
+CFG_REG_LSH = 0x12
+CFG_REG_ORD = 0x13
diff --git a/tools/testing/kddv/kddv/core/mockup.py b/tools/testing/kddv/kddv/core/mockup.py
index b5e6c83c9164..8bea2db97232 100755
--- a/tools/testing/kddv/kddv/core/mockup.py
+++ b/tools/testing/kddv/kddv/core/mockup.py
@@ -29,6 +29,7 @@ class Mockup(object):
         self.bpf = p.bpf
         self.addr = p.address
         self.devid = p.device_id
+        self.configs = p.configs
         self.regshift = p.regshift
         self.regbytes = p.regbytes
         self.valbytes = p.valbytes
@@ -103,6 +104,7 @@ class Mockup(object):
     def load(self):
         self.load_bpf()
         self.load_regmaps()
+        self.load_configs()
         self.create_device()
 
     def unload(self):
@@ -116,6 +118,32 @@ class Mockup(object):
     def to_bpf_bytes(self, val, len):
         return list("%d" % n for n in list(val.to_bytes(len, 'little')))
 
+    def write_bpf_map(self, name, addr, val):
+        cmds = [self.bpftool, 'map', 'update']
+        cmds += ['name', name]
+        cmds += ['key']
+        cmds += self.to_bpf_bytes(addr, 4)
+        cmds += ['value']
+        cmds += self.to_bpf_bytes(val, 4)
+        logger.debug(' '.join(cmds))
+        subprocess.check_output(cmds)
+
+    def write_config(self, addr, val):
+        if self.bpf is None:
+            return
+        self.write_bpf_map('bpf_xfer_conf', addr, val)
+
+    def write_configs(self, addr, data):
+        for i in range(len(data)):
+            self.write_config(addr + i, data[i])
+
+    def load_configs(self):
+        for reg, value in self.configs.items():
+            if isinstance(value, list):
+                self.write_configs(reg, value)
+            else:
+                self.write_config(reg, value)
+
     def load_regmaps(self):
         for reg, value in self.regmaps.items():
             if isinstance(value, list):
diff --git a/tools/testing/kddv/kddv/core/model.py b/tools/testing/kddv/kddv/core/model.py
index 9da4716df7dc..494b69566536 100755
--- a/tools/testing/kddv/kddv/core/model.py
+++ b/tools/testing/kddv/kddv/core/model.py
@@ -67,6 +67,10 @@ class DriverModel(object):
     def valbytes(self):
         return 1
 
+    @property
+    def configs(self):
+        return {}
+
     @property
     def regmaps(self):
         return {}
diff --git a/tools/testing/kddv/kddv/data/bpf/include/bpf-xfer-conf.h b/tools/testing/kddv/kddv/data/bpf/include/bpf-xfer-conf.h
new file mode 100644
index 000000000000..49adbcc6a1af
--- /dev/null
+++ b/tools/testing/kddv/kddv/data/bpf/include/bpf-xfer-conf.h
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * The bpf program of control xfer configuration
+ *
+ * Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+ */
+
+#ifndef __BPF_XFER_CONF_
+#define __BPF_XFER_CONF_
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_core_read.h>
+
+#define CONF_REGS_SIZE		0x100
+
+#define BPF_CONF_REG_MASK	0x10
+#define BPF_CONF_REG_RSHIFT	0x11
+#define BPF_CONF_REG_LSHIFT	0x12
+#define BPF_CONF_REG_XBSWAP	0x13
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, CONF_REGS_SIZE);
+	__type(key, __u32);
+	__type(value, __u32);
+} bpf_xfer_conf SEC(".maps");
+
+static u32 bpf_reg_mask, bpf_reg_xbswap;
+static u32 bpf_reg_rshift, bpf_reg_lshift;
+
+static u32 bpf_xfer_read_conf(u32 key)
+{
+	u32 *reg;
+
+	reg = bpf_map_lookup_elem(&bpf_xfer_conf, &key);
+	if (!reg) {
+		bpf_printk("config key %d not exists", key);
+		return 0;
+	}
+
+	return *reg;
+}
+
+static int bpf_xfer_write_conf(u32 key, u32 value)
+{
+	if (bpf_map_update_elem(&bpf_xfer_conf, &key, &value,
+				BPF_EXIST)) {
+		bpf_printk("config key %d not exists", key);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int bpf_xfer_update_config(void)
+{
+	bpf_reg_mask = bpf_xfer_read_conf(BPF_CONF_REG_MASK);
+	bpf_reg_rshift = bpf_xfer_read_conf(BPF_CONF_REG_RSHIFT);
+	bpf_reg_lshift = bpf_xfer_read_conf(BPF_CONF_REG_LSHIFT);
+	bpf_reg_xbswap = bpf_xfer_read_conf(BPF_CONF_REG_XBSWAP);
+
+	return 0;
+}
+
+u8 bpf_xfer_reg_u8(u8 reg)
+{
+	reg = reg & (bpf_reg_mask ? bpf_reg_mask : 0xff);
+	if (bpf_reg_rshift)
+		reg = reg >> bpf_reg_rshift;
+	if (bpf_reg_lshift)
+		reg = reg << bpf_reg_lshift;
+	return reg;
+}
+
+u16 bpf_xfer_reg_u16(u16 reg)
+{
+	if (bpf_reg_xbswap)
+		reg = __builtin_bswap16(reg);
+	reg = reg & (bpf_reg_mask ? bpf_reg_mask : 0x7fff);
+	if (bpf_reg_rshift)
+		reg = reg >> bpf_reg_rshift;
+	if (bpf_reg_lshift)
+		reg = reg << bpf_reg_lshift;
+	return reg;
+}
+
+u32 bpf_xfer_reg_u24(u32 reg)
+{
+	if (bpf_reg_xbswap)
+		reg = __builtin_bswap32(reg);
+	reg = reg & (bpf_reg_mask ? bpf_reg_mask : 0x7fffff);
+	if (bpf_reg_rshift)
+		reg = reg >> bpf_reg_rshift;
+	if (bpf_reg_lshift)
+		reg = reg << bpf_reg_lshift;
+	return reg;
+}
+
+u32 bpf_xfer_reg_u32(u32 reg)
+{
+	if (bpf_reg_xbswap)
+		reg = __builtin_bswap32(reg);
+	reg = reg & (bpf_reg_mask ? bpf_reg_mask : 0x7fffffff);
+	if (bpf_reg_rshift)
+		reg = reg >> bpf_reg_rshift;
+	if (bpf_reg_lshift)
+		reg = reg << bpf_reg_lshift;
+	return reg;
+}
+#endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH -next 03/14] kddv/core: Add io fault support to bpf program
  2023-11-18 10:40 [PATCH -next 00/14] Implement a ligth weight device driver test framework Zhang Xiaoxu
  2023-11-18 10:40 ` [PATCH -next 01/14] kddv/core: " Zhang Xiaoxu
  2023-11-18 10:40 ` [PATCH -next 02/14] kddv/core: Allow test case config bpf program Zhang Xiaoxu
@ 2023-11-18 10:40 ` Zhang Xiaoxu
  2023-11-18 10:40 ` [PATCH -next 04/14] kddv/core: Check kmsg before return from test case Zhang Xiaoxu
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Zhang Xiaoxu @ 2023-11-18 10:40 UTC (permalink / raw)
  To: zhangxiaoxu5, weiyongjun1, linux-kernel, broonie, rostedt, mingo,
	frowand.list, linux-spi

From: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>

Add common io fault interface, then the bpf program can use
it to mock the hardware io error.

Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>
---
 tools/testing/kddv/kddv/core/consts.py              |  1 +
 tools/testing/kddv/kddv/core/model.py               |  4 ++++
 .../kddv/kddv/data/bpf/include/bpf-xfer-conf.h      | 13 +++++++++++++
 3 files changed, 18 insertions(+)

diff --git a/tools/testing/kddv/kddv/core/consts.py b/tools/testing/kddv/kddv/core/consts.py
index 22abd7fc655c..b761407f5e88 100755
--- a/tools/testing/kddv/kddv/core/consts.py
+++ b/tools/testing/kddv/kddv/core/consts.py
@@ -10,3 +10,4 @@ CFG_REG_MASK = 0x10
 CFG_REG_RSH = 0x11
 CFG_REG_LSH = 0x12
 CFG_REG_ORD = 0x13
+CFG_IO_FAULT = 0x20
diff --git a/tools/testing/kddv/kddv/core/model.py b/tools/testing/kddv/kddv/core/model.py
index 494b69566536..ff782c20313d 100755
--- a/tools/testing/kddv/kddv/core/model.py
+++ b/tools/testing/kddv/kddv/core/model.py
@@ -8,6 +8,7 @@
 
 from .driver import Driver
 from .mockup import Mockup
+from .consts import CFG_IO_FAULT
 
 class DriverModel(object):
     bus = None
@@ -89,3 +90,6 @@ class DriverModel(object):
 
     def write_regs(self, addr, data):
         self.mockup.write_regs(addr, data)
+
+    def trigger_io_fault(self, count = 1):
+        self.mockup.write_config(CFG_IO_FAULT, count)
diff --git a/tools/testing/kddv/kddv/data/bpf/include/bpf-xfer-conf.h b/tools/testing/kddv/kddv/data/bpf/include/bpf-xfer-conf.h
index 49adbcc6a1af..6a09bd391641 100644
--- a/tools/testing/kddv/kddv/data/bpf/include/bpf-xfer-conf.h
+++ b/tools/testing/kddv/kddv/data/bpf/include/bpf-xfer-conf.h
@@ -18,6 +18,7 @@
 #define BPF_CONF_REG_RSHIFT	0x11
 #define BPF_CONF_REG_LSHIFT	0x12
 #define BPF_CONF_REG_XBSWAP	0x13
+#define BPF_CONF_IO_FAULT	0x20
 
 struct {
 	__uint(type, BPF_MAP_TYPE_ARRAY);
@@ -28,6 +29,7 @@ struct {
 
 static u32 bpf_reg_mask, bpf_reg_xbswap;
 static u32 bpf_reg_rshift, bpf_reg_lshift;
+static u32 bpf_io_fault;
 
 static u32 bpf_xfer_read_conf(u32 key)
 {
@@ -59,6 +61,7 @@ static int bpf_xfer_update_config(void)
 	bpf_reg_rshift = bpf_xfer_read_conf(BPF_CONF_REG_RSHIFT);
 	bpf_reg_lshift = bpf_xfer_read_conf(BPF_CONF_REG_LSHIFT);
 	bpf_reg_xbswap = bpf_xfer_read_conf(BPF_CONF_REG_XBSWAP);
+	bpf_io_fault = bpf_xfer_read_conf(BPF_CONF_IO_FAULT);
 
 	return 0;
 }
@@ -108,4 +111,14 @@ u32 bpf_xfer_reg_u32(u32 reg)
 		reg = reg << bpf_reg_lshift;
 	return reg;
 }
+
+bool bpf_xfer_should_fault(void)
+{
+	bpf_xfer_update_config();
+
+	if (bpf_io_fault)
+		bpf_xfer_write_conf(BPF_CONF_IO_FAULT, bpf_io_fault - 1);
+
+	return !!bpf_io_fault;
+}
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH -next 04/14] kddv/core: Check kmsg before return from test case
  2023-11-18 10:40 [PATCH -next 00/14] Implement a ligth weight device driver test framework Zhang Xiaoxu
                   ` (2 preceding siblings ...)
  2023-11-18 10:40 ` [PATCH -next 03/14] kddv/core: Add io fault support to " Zhang Xiaoxu
@ 2023-11-18 10:40 ` Zhang Xiaoxu
  2023-11-18 10:40 ` [PATCH -next 05/14] kddv/core: Support kernel memory leak detector Zhang Xiaoxu
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Zhang Xiaoxu @ 2023-11-18 10:40 UTC (permalink / raw)
  To: zhangxiaoxu5, weiyongjun1, linux-kernel, broonie, rostedt, mingo,
	frowand.list, linux-spi

From: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>

Check kernel message before return from test case, if bug
or error in kernel message, fail the test case.

Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>
---
 tools/testing/kddv/kddv/core/ddunit.py  | 12 ++++++++
 tools/testing/kddv/kddv/core/dmesg.py   | 41 +++++++++++++++++++++++++
 tools/testing/kddv/kddv/core/environ.py | 28 +++++++++++++++++
 3 files changed, 81 insertions(+)
 create mode 100755 tools/testing/kddv/kddv/core/dmesg.py
 create mode 100755 tools/testing/kddv/kddv/core/environ.py

diff --git a/tools/testing/kddv/kddv/core/ddunit.py b/tools/testing/kddv/kddv/core/ddunit.py
index fd4ab9fe048c..197300f8a6d4 100755
--- a/tools/testing/kddv/kddv/core/ddunit.py
+++ b/tools/testing/kddv/kddv/core/ddunit.py
@@ -10,6 +10,7 @@ import logging
 import unittest
 
 from .model import DriverModel
+from .environ import environ
 
 logger = logging.getLogger(__name__)
 
@@ -25,12 +26,18 @@ class DriverTest(unittest.TestCase, DriverModel):
         except:
             self.skipTest(f"Module {self.module_name} not found")
         self.mockup.load()
+        environ.setup()
 
     def tearDown(self):
+        environ.teardown()
         self.mockup.unload()
         self.driver.teardown()
         super().tearDown()
 
+    def _callTestMethod(self, method):
+        method()
+        self.assertFault()
+
     def assertRegEqual(self, reg, data, msg=None):
         value = self.read_reg(reg)
         self.assertEqual(value, data, msg)
@@ -42,3 +49,8 @@ class DriverTest(unittest.TestCase, DriverModel):
     def assertRegsEqual(self, reg, data, msg=None):
         value = self.read_regs(reg, len(data))
         self.assertListEqual(value, data, msg)
+
+    def assertFault(self):
+        msg = environ.check_failure()
+        if msg:
+            raise self.failureException(msg)
diff --git a/tools/testing/kddv/kddv/core/dmesg.py b/tools/testing/kddv/kddv/core/dmesg.py
new file mode 100755
index 000000000000..e528b6164f46
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/dmesg.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+import re
+import logging
+import subprocess
+
+KERNEL_PANIC = [
+    "BUG:",
+    "WARNING:",
+    "INFO:",
+    "[kK]ernel BUG",
+    "PANIC: double fault",
+    "divide error:",
+    "UBSAN:",
+    "Unable to handle kernel",
+    "general protection fault",
+]
+
+logger = logging.getLogger(__name__)
+
+class KernelMessage(object):
+
+    def setup(self):
+        subprocess.run(["/usr/bin/dmesg", "-C"])
+
+    def teardown(self):
+        subprocess.run(["/usr/bin/dmesg", "-C"])
+
+    def check_failure(self):
+        logger.debug('check kernel message')
+        kmsg = subprocess.check_output(["/usr/bin/dmesg", "-c"])
+        regex_pattern = re.compile("|".join(KERNEL_PANIC))
+        if regex_pattern.search(kmsg.decode()):
+            return kmsg.decode()
+        return None
diff --git a/tools/testing/kddv/kddv/core/environ.py b/tools/testing/kddv/kddv/core/environ.py
new file mode 100755
index 000000000000..d57e33a6ac6e
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/environ.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+import logging
+
+from .dmesg import KernelMessage
+
+logger = logging.getLogger(__name__)
+
+class Environ(object):
+    def __init__(self):
+        self.kmsg = KernelMessage()
+
+    def setup(self):
+        self.kmsg.setup()
+
+    def teardown(self):
+        self.kmsg.teardown()
+
+    def check_failure(self):
+        return self.kmsg.check_failure()
+
+environ = Environ()
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH -next 05/14] kddv/core: Support kernel memory leak detector
  2023-11-18 10:40 [PATCH -next 00/14] Implement a ligth weight device driver test framework Zhang Xiaoxu
                   ` (3 preceding siblings ...)
  2023-11-18 10:40 ` [PATCH -next 04/14] kddv/core: Check kmsg before return from test case Zhang Xiaoxu
@ 2023-11-18 10:40 ` Zhang Xiaoxu
  2023-11-18 10:40 ` [PATCH -next 06/14] kddv/core: Add page and slab fault inject support Zhang Xiaoxu
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Zhang Xiaoxu @ 2023-11-18 10:40 UTC (permalink / raw)
  To: zhangxiaoxu5, weiyongjun1, linux-kernel, broonie, rostedt, mingo,
	frowand.list, linux-spi

From: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>

Check the kmemleak before return from test case,
fail the test case if a kmemleak is detected.

Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>
---
 tools/testing/kddv/kddv/core/environ.py | 13 ++++++++-
 tools/testing/kddv/kddv/core/memleak.py | 39 +++++++++++++++++++++++++
 2 files changed, 51 insertions(+), 1 deletion(-)
 create mode 100755 tools/testing/kddv/kddv/core/memleak.py

diff --git a/tools/testing/kddv/kddv/core/environ.py b/tools/testing/kddv/kddv/core/environ.py
index d57e33a6ac6e..68c98e8c44da 100755
--- a/tools/testing/kddv/kddv/core/environ.py
+++ b/tools/testing/kddv/kddv/core/environ.py
@@ -9,20 +9,31 @@
 import logging
 
 from .dmesg import KernelMessage
+from .memleak import Kmemleak
 
 logger = logging.getLogger(__name__)
 
 class Environ(object):
     def __init__(self):
         self.kmsg = KernelMessage()
+        self.leak = Kmemleak()
 
     def setup(self):
         self.kmsg.setup()
+        self.leak.setup()
 
     def teardown(self):
+        self.leak.teardown()
         self.kmsg.teardown()
 
+    def enable_kmemleak(self):
+        """Enable Kernel memory leak detector"""
+        self.leak.enabled = True
+
     def check_failure(self):
-        return self.kmsg.check_failure()
+        msg = self.kmsg.check_failure()
+        if msg:
+            return msg
+        return self.leak.check_failure()
 
 environ = Environ()
diff --git a/tools/testing/kddv/kddv/core/memleak.py b/tools/testing/kddv/kddv/core/memleak.py
new file mode 100755
index 000000000000..7ff67997560f
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/memleak.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+import os
+import logging
+import pathlib
+
+KMEMLEAK = '/sys/kernel/debug/kmemleak'
+
+logger = logging.getLogger(__name__)
+
+class Kmemleak(object):
+    def __init__(self):
+        self.has_feature = os.path.exists(KMEMLEAK)
+        self.ctrl = pathlib.Path(KMEMLEAK)
+        self.enabled = False
+
+    def setup(self):
+        if not self.has_feature or not self.enabled:
+            return
+        self.ctrl.write_text('clear')
+
+    def teardown(self):
+        if not self.has_feature or not self.enabled:
+            return
+        self.ctrl.write_text('clear')
+
+    def check_failure(self):
+        if not self.has_feature or not self.enabled:
+            return None
+        logger.debug('check kernel memleak')
+        self.ctrl.write_text('scan')
+        self.ctrl.write_text('scan')
+        return self.ctrl.read_text().rstrip()
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH -next 06/14] kddv/core: Add page and slab fault inject support
  2023-11-18 10:40 [PATCH -next 00/14] Implement a ligth weight device driver test framework Zhang Xiaoxu
                   ` (4 preceding siblings ...)
  2023-11-18 10:40 ` [PATCH -next 05/14] kddv/core: Support kernel memory leak detector Zhang Xiaoxu
@ 2023-11-18 10:40 ` Zhang Xiaoxu
  2023-11-18 10:40 ` [PATCH -next 07/14] kddv/cmd: Add command to create/remove mockup device Zhang Xiaoxu
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Zhang Xiaoxu @ 2023-11-18 10:40 UTC (permalink / raw)
  To: zhangxiaoxu5, weiyongjun1, linux-kernel, broonie, rostedt, mingo,
	frowand.list, linux-spi

From: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>

Implement page and slab fault inject, to test whether the drivers
can work properly when out of memory.

Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>
---
 tools/testing/kddv/kddv/core/ddunit.py        | 29 ++++++-
 tools/testing/kddv/kddv/core/driver.py        |  3 +
 tools/testing/kddv/kddv/core/environ.py       | 26 ++++++
 tools/testing/kddv/kddv/core/failnth.py       | 57 ++++++++++++
 tools/testing/kddv/kddv/core/faulter.py       | 48 +++++++++++
 .../testing/kddv/kddv/core/faults/__init__.py | 13 +++
 tools/testing/kddv/kddv/core/faults/fail.py   | 86 +++++++++++++++++++
 tools/testing/kddv/kddv/core/faults/page.py   | 40 +++++++++
 tools/testing/kddv/kddv/core/faults/slab.py   | 36 ++++++++
 9 files changed, 337 insertions(+), 1 deletion(-)
 create mode 100755 tools/testing/kddv/kddv/core/failnth.py
 create mode 100755 tools/testing/kddv/kddv/core/faulter.py
 create mode 100755 tools/testing/kddv/kddv/core/faults/__init__.py
 create mode 100755 tools/testing/kddv/kddv/core/faults/fail.py
 create mode 100755 tools/testing/kddv/kddv/core/faults/page.py
 create mode 100755 tools/testing/kddv/kddv/core/faults/slab.py

diff --git a/tools/testing/kddv/kddv/core/ddunit.py b/tools/testing/kddv/kddv/core/ddunit.py
index 197300f8a6d4..716e0e1505d1 100755
--- a/tools/testing/kddv/kddv/core/ddunit.py
+++ b/tools/testing/kddv/kddv/core/ddunit.py
@@ -11,9 +11,26 @@ import unittest
 
 from .model import DriverModel
 from .environ import environ
+from .failnth import FaultIterator
 
 logger = logging.getLogger(__name__)
 
+class _AssertRaisesFaultContext(unittest.case._AssertRaisesContext):
+    def __enter__(self):
+        environ.enter_fault_inject()
+        return self
+
+    def __exit__(self, exc_type, exc_value, tb):
+        if not environ.exit_fault_inject():
+            return False
+        if exc_type is None:
+            return True
+        if issubclass(exc_type, self.expected):
+            return True
+        if issubclass(exc_type, AssertionError):
+            return True
+        return super().__exit__(exc_type, exc_value, tb)
+
 class DriverTest(unittest.TestCase, DriverModel):
     def __init__(self, methodName=None):
         super().__init__(methodName)
@@ -35,7 +52,10 @@ class DriverTest(unittest.TestCase, DriverModel):
         super().tearDown()
 
     def _callTestMethod(self, method):
-        method()
+        fault = FaultIterator()
+        for nth in iter(fault):
+            logger.debug(f"fault inject: nth={nth}")
+            method()
         self.assertFault()
 
     def assertRegEqual(self, reg, data, msg=None):
@@ -54,3 +74,10 @@ class DriverTest(unittest.TestCase, DriverModel):
         msg = environ.check_failure()
         if msg:
             raise self.failureException(msg)
+
+    def assertRaisesFault(self, *args, **kwargs):
+        context = _AssertRaisesFaultContext(OSError, self)
+        try:
+            return context.handle('assertRaises', args, kwargs)
+        finally:
+            context = None
diff --git a/tools/testing/kddv/kddv/core/driver.py b/tools/testing/kddv/kddv/core/driver.py
index 55ad804068b5..a6fcf3dcdd7d 100755
--- a/tools/testing/kddv/kddv/core/driver.py
+++ b/tools/testing/kddv/kddv/core/driver.py
@@ -12,6 +12,7 @@ import subprocess
 from pathlib import Path
 
 from .device import Device
+from .environ import environ
 
 logger = logging.getLogger(__name__)
 
@@ -54,9 +55,11 @@ class Driver(object):
         subprocess.check_output(
             ["/sbin/modprobe", self.module], stderr=subprocess.STDOUT
         )
+        environ.notify_insmod(self.module)
 
     def remove_mdule(self):
         logger.debug(f'rmmod {self.module}')
+        environ.notify_rmmod()
         subprocess.check_output(["/sbin/rmmod", self.module])
 
     def setup(self):
diff --git a/tools/testing/kddv/kddv/core/environ.py b/tools/testing/kddv/kddv/core/environ.py
index 68c98e8c44da..8bd7d6a60cb7 100755
--- a/tools/testing/kddv/kddv/core/environ.py
+++ b/tools/testing/kddv/kddv/core/environ.py
@@ -9,6 +9,7 @@
 import logging
 
 from .dmesg import KernelMessage
+from .faulter import FaultInject
 from .memleak import Kmemleak
 
 logger = logging.getLogger(__name__)
@@ -17,12 +18,15 @@ class Environ(object):
     def __init__(self):
         self.kmsg = KernelMessage()
         self.leak = Kmemleak()
+        self.fault = FaultInject()
 
     def setup(self):
         self.kmsg.setup()
         self.leak.setup()
+        self.fault.setup()
 
     def teardown(self):
+        self.fault.teardown()
         self.leak.teardown()
         self.kmsg.teardown()
 
@@ -36,4 +40,26 @@ class Environ(object):
             return msg
         return self.leak.check_failure()
 
+    def enable_fault_inject(self, feature):
+        """Enable fault injection feature"""
+        self.fault.enable_feature(feature)
+
+    def fault_running(self):
+        """Fault injection has been enabled"""
+        return self.fault.running
+
+    def enter_fault_inject(self):
+        """Enter fault injection"""
+        self.fault.start_features()
+
+    def exit_fault_inject(self):
+        """Exit fault injection"""
+        return self.fault.stop_features()
+
+    def notify_insmod(self, name):
+        self.fault.filter_module(name)
+
+    def notify_rmmod(self):
+        self.fault.filter_module(None)
+
 environ = Environ()
diff --git a/tools/testing/kddv/kddv/core/failnth.py b/tools/testing/kddv/kddv/core/failnth.py
new file mode 100755
index 000000000000..6d547aabb0a1
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/failnth.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+import os
+import re
+import logging
+
+from pathlib import Path
+from .environ import environ
+
+logger = logging.getLogger(__name__)
+
+class FaultIterator(object):
+    def __init__(self, max_loop = 0):
+        self._max_loop = max_loop
+        self._cur_fail = 0
+        self._max_fail = 3
+        self.path = Path(f"/proc/self/fail-nth")
+
+    def __iter__(self):
+        self.nth = -1
+        return self
+
+    def __next__(self):
+        self.nth += 1
+        if not self.nth:
+            return self.nth
+        if not environ.fault_running():
+            logger.debug('fault inject not running')
+            raise StopIteration
+        if not os.path.exists(self.path):
+            logger.debug('fault inject not exists')
+            raise StopIteration
+        if self._max_loop and self._max_loop < self.nth:
+            raise StopIteration
+        if self.read_nth() > 0:
+            self.write_nth(0)
+            self._cur_fail += 1
+            if self._cur_fail >= self._max_fail:
+                logger.debug('end fault inject')
+                raise StopIteration
+        else:
+           self._cur_fail = 0
+        self.write_nth(self.nth)
+        return self.nth
+
+    def read_nth(self):
+        return int(self.path.read_text().rstrip())
+
+    def write_nth(self, val):
+        logger.debug(f"write {val} to fail-nth")
+        self.path.write_text(str(val))
diff --git a/tools/testing/kddv/kddv/core/faulter.py b/tools/testing/kddv/kddv/core/faulter.py
new file mode 100755
index 000000000000..32f4d5c3a57c
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/faulter.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+from .faults import FailModule
+
+class FaultInject(object):
+    def __init__(self):
+        self.enabled = False
+        self.running = False
+        self.features = []
+        for subclass in FailModule.__subclasses__():
+            self.features.append(subclass())
+
+    def setup(self):
+        pass
+
+    def teardown(self):
+        self.running = False
+
+    def start_features(self):
+        if not self.enabled:
+            return
+        for feature in self.features:
+            feature.start()
+        self.running = True
+
+    def stop_features(self):
+        if not self.enabled:
+            return False
+        for feature in self.features:
+            feature.stop()
+        return True
+
+    def filter_module(self, module):
+        for feature in self.features:
+            feature.filter_module(module)
+
+    def enable_feature(self, name):
+        for feature in self.features:
+            if name in [feature.key, 'all']:
+                if feature.has_support:
+                    feature.enabled = True
+                    self.enabled = True
diff --git a/tools/testing/kddv/kddv/core/faults/__init__.py b/tools/testing/kddv/kddv/core/faults/__init__.py
new file mode 100755
index 000000000000..2e1280ca20da
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/faults/__init__.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+__all__ = ['FailModule']
+
+from .fail import FailModule
+from .slab import FailSlab
+from .page import FailPage
diff --git a/tools/testing/kddv/kddv/core/faults/fail.py b/tools/testing/kddv/kddv/core/faults/fail.py
new file mode 100755
index 000000000000..4d4344e00e9a
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/faults/fail.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+import re
+import os
+import logging
+from pathlib import Path
+
+logger = logging.getLogger(__name__)
+
+class FailModule(object):
+    name = None
+    key = None
+
+    def __init__(self):
+        self.has_support = os.path.exists(f"/sys/kernel/debug/{self.name}")
+        self.ftext = Path(f"/sys/kernel/debug/{self.name}/require-start")
+        self.fdata = Path(f"/sys/kernel/debug/{self.name}/require-end")
+        self.fdepth = Path(f"/sys/kernel/debug/{self.name}/stacktrace-depth")
+        self.nowarn = Path(f"/sys/kernel/debug/{self.name}/verbose")
+        self.enabled = False
+        self.module = None
+
+    def feature_enabled(self):
+        if not self.has_support:
+            return False
+        return self.enabled
+
+    def filter_module(self, name):
+        if name is None:
+            self.module = None
+        else:
+            self.module = re.sub('-', '_', name)
+
+    def enable_verbose(self):
+        if not self.feature_enabled():
+            return
+        self.nowarn.write_text('1')
+
+    def disable_verbose(self):
+        if not self.feature_enabled():
+            return
+        self.nowarn.write_text('0')
+
+    def enable_module_filter(self):
+        if not self.feature_enabled():
+            return
+        if self.module is None:
+            return
+        logger.debug(f"enter module filter for fail {self.name}")
+        mtext = Path(f"/sys/module/{self.module}/sections/.text")
+        mdata = Path(f"/sys/module/{self.module}/sections/.data")
+        self.ftext.write_text(mtext.read_text().rstrip())
+        self.fdata.write_text(mdata.read_text().rstrip())
+        self.fdepth.write_text('32')
+
+    def disable_module_filter(self):
+        if not self.feature_enabled():
+            return
+        if self.module is None:
+            return
+        logger.debug(f"exit module filter for fail {self.name}")
+        self.ftext.write_text('0')
+        self.fdata.write_text('0')
+        self.fdepth.write_text('32')
+
+    def enable_feature(self):
+        pass
+
+    def disable_feature(self):
+        pass
+
+    def start(self):
+        self.enable_module_filter()
+        self.enable_verbose()
+        self.enable_feature()
+
+    def stop(self):
+        self.disable_feature()
+        self.disable_verbose()
+        self.disable_module_filter()
diff --git a/tools/testing/kddv/kddv/core/faults/page.py b/tools/testing/kddv/kddv/core/faults/page.py
new file mode 100755
index 000000000000..e78f6e1a8c88
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/faults/page.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+import logging
+from pathlib import Path
+
+from .fail import FailModule
+
+FAILPAGE_IGNORE_HMEM = '/sys/kernel/debug/fail_page_alloc/ignore-gfp-highmem'
+FAILPAGE_IGNORE_WAIT = '/sys/kernel/debug/fail_page_alloc/ignore-gfp-wait'
+
+logger = logging.getLogger(__name__)
+
+class FailPage(FailModule):
+    name = 'fail_page_alloc'
+    key = 'page'
+
+    def __init__(self):
+        super().__init__()
+        self.ignore_hmem = Path(FAILPAGE_IGNORE_HMEM)
+        self.ignore_wait = Path(FAILPAGE_IGNORE_WAIT)
+
+    def enable_feature(self):
+        if not self.feature_enabled():
+            return
+        logger.debug("enter fail page injection")
+        self.ignore_hmem.write_text('N')
+        self.ignore_wait.write_text('N')
+
+    def disable_feature(self):
+        if not self.feature_enabled():
+            return
+        logger.debug("exit fail page injection")
+        self.ignore_hmem.write_text('Y')
+        self.ignore_wait.write_text('Y')
diff --git a/tools/testing/kddv/kddv/core/faults/slab.py b/tools/testing/kddv/kddv/core/faults/slab.py
new file mode 100755
index 000000000000..5472f1ec4795
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/faults/slab.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+import logging
+from pathlib import Path
+
+from .fail import FailModule
+
+FAILSLAB_IGNORE = '/sys/kernel/debug/failslab/ignore-gfp-wait'
+
+logger = logging.getLogger(__name__)
+
+class FailSlab(FailModule):
+    name = 'failslab'
+    key = 'slab'
+
+    def __init__(self):
+        super().__init__()
+        self.ignore = Path(FAILSLAB_IGNORE)
+
+    def enable_feature(self):
+        if not self.feature_enabled():
+            return
+        logger.debug("enter fail slab injection")
+        self.ignore.write_text('N')
+
+    def disable_feature(self):
+        if not self.feature_enabled():
+            return
+        logger.debug("exit fail slab injection")
+        self.ignore.write_text('Y')
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH -next 07/14] kddv/cmd: Add command to create/remove mockup device
  2023-11-18 10:40 [PATCH -next 00/14] Implement a ligth weight device driver test framework Zhang Xiaoxu
                   ` (5 preceding siblings ...)
  2023-11-18 10:40 ` [PATCH -next 06/14] kddv/core: Add page and slab fault inject support Zhang Xiaoxu
@ 2023-11-18 10:40 ` Zhang Xiaoxu
  2023-11-18 10:40 ` [PATCH -next 08/14] kddv/cmd: Add command to run testcases Zhang Xiaoxu
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Zhang Xiaoxu @ 2023-11-18 10:40 UTC (permalink / raw)
  To: zhangxiaoxu5, weiyongjun1, linux-kernel, broonie, rostedt, mingo,
	frowand.list, linux-spi

From: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>

Add command kddv.cmds.mock to create/remove mockup device which support
by the test framework. Usage:

Create device:
  $ python3 -m kddv.cmds.mock --bus spi --devid mchp23k256
  create spi device mchp23k256 success!

Then the mockup device can be accessed by exists user space tools.
  $ ls /dev/mtd0
  mtd0    mtd0ro
  $ hexdump /dev/mtd0
  0000000 0000 0000 0000 0000 0000 0000 0000 0000
  *
  0008000

Remove the mockup device:
 $ python3 -m kddv.cmds.mock --bus spi --devid mchp23k256 -r
 [  198.718172] Deleting MTD partitions on "spi0.0":
 remove spi device mchp23k256 success!

Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>
---
 tools/testing/kddv/kddv/Makefile         |   1 +
 tools/testing/kddv/kddv/cmds/__init__.py |   0
 tools/testing/kddv/kddv/cmds/mock.py     | 105 +++++++++++++++++++++++
 tools/testing/kddv/kddv/cmds/utils.py    |  28 ++++++
 4 files changed, 134 insertions(+)
 create mode 100755 tools/testing/kddv/kddv/cmds/__init__.py
 create mode 100755 tools/testing/kddv/kddv/cmds/mock.py
 create mode 100755 tools/testing/kddv/kddv/cmds/utils.py

diff --git a/tools/testing/kddv/kddv/Makefile b/tools/testing/kddv/kddv/Makefile
index a68112154669..a5c91fcb0e9a 100644
--- a/tools/testing/kddv/kddv/Makefile
+++ b/tools/testing/kddv/kddv/Makefile
@@ -12,6 +12,7 @@ install:
 	$(INSTALL) -m 0755 -d $(INSTALL_PATH)
 	$(INSTALL) __init__.py $(INSTALL_PATH)
 	cp -rf core/ $(INSTALL_PATH)
+	cp -rf cmds/ $(INSTALL_PATH)
 	cp -rf tests/ $(INSTALL_PATH)
 
 clean:
diff --git a/tools/testing/kddv/kddv/cmds/__init__.py b/tools/testing/kddv/kddv/cmds/__init__.py
new file mode 100755
index 000000000000..e69de29bb2d1
diff --git a/tools/testing/kddv/kddv/cmds/mock.py b/tools/testing/kddv/kddv/cmds/mock.py
new file mode 100755
index 000000000000..2ec5e45219a0
--- /dev/null
+++ b/tools/testing/kddv/kddv/cmds/mock.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+import os
+import sys
+import logging
+import argparse
+import unittest
+import subprocess
+
+from kddv.core.mockup import Mockup
+from kddv.core.ddunit import DriverTest
+from kddv.core.buses import *
+from . import utils
+
+ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+logger = logging.getLogger()
+
+def search(suite, bus: str, devid: str):
+    mdrv = None
+    for t in suite:
+        if isinstance(t, unittest.TestSuite):
+            driver = search(t, bus, devid)
+            if driver:
+                if driver.device_id == devid:
+                    return driver
+                mdrv = driver
+        elif isinstance(t, DriverTest):
+            if not hasattr(t, 'bus') or not hasattr(t, 'device_id'):
+                logger.debug(f"not a driver test case: {t}")
+                continue
+            if t.bus != bus:
+                continue
+            if t.device_id == devid:
+                return t
+            if  t.driver_name == devid:
+                mdrv = t
+        else:
+            return mdrv
+
+def do_mockup_device(t):
+    mock = Mockup.create(t.bus, t)
+    try:
+        subprocess.check_output(
+            ["/sbin/modprobe", t.module_name], stderr=subprocess.STDOUT
+        )
+    except:
+        logger.warning(f"Module {t.module_name} not found")
+        sys.exit(1)
+
+    mock.load()
+    logger.warning(f"create {t.bus} device {t.device_id} success!")
+
+def do_remove_device(t):
+    mock = Mockup.create(t.bus, t)
+    try:
+        mock.unload()
+        logger.warning(f"remove {t.bus} device {t.device_id} success!")
+    except:
+        logger.warning(f"{t.bus} device {t.device_id} not found")
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--bus", "-b", type=str, required=True,
+        choices=["i2c", "spi", "pci", "platform"], help="Bus Types"
+    )
+    parser.add_argument(
+        "--devid", "-d", type=str, required=True, help="Device ID"
+    )
+    parser.add_argument(
+        "--log-level", "-l", type=str, default=None,
+        choices=utils.LOG_LEVELS, help="Log level"
+    )
+    parser.add_argument(
+        "--remove", "-r", action='store_true', default=False,
+        help="Remove device",
+    )
+    args = parser.parse_args()
+
+    if args.log_level:
+        utils.setup_logger(args.log_level)
+
+    loader = unittest.defaultTestLoader
+    suites = loader.discover(os.path.join(ROOT_DIR, 'tests'))
+    driver = search(suites, args.bus, args.devid)
+    if driver is None:
+        logger.error(f"{args.bus} device {args.devid} not support")
+        sys.exit(1)
+
+    if not args.remove:
+        do_mockup_device(driver)
+    else:
+        do_remove_device(driver)
+
+    sys.exit(0)
+
+if __name__ == "__main__":
+    main()
diff --git a/tools/testing/kddv/kddv/cmds/utils.py b/tools/testing/kddv/kddv/cmds/utils.py
new file mode 100755
index 000000000000..8130d7a57a36
--- /dev/null
+++ b/tools/testing/kddv/kddv/cmds/utils.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+import sys
+import logging
+
+logger = logging.getLogger()
+
+LOG_FORMAT = "%(asctime)-15s [%(levelname)-7s] %(message)s"
+LOG_LEVELS = {
+    'ERROR': logging.ERROR,
+    'WARN': logging.WARN,
+    'INFO': logging.INFO,
+    'DEBUG': logging.DEBUG
+}
+
+def setup_logger(level):
+    logger.setLevel(LOG_LEVELS.get(level))
+    handler = logging.StreamHandler(sys.stdout)
+    handler.setFormatter(logging.Formatter(
+        fmt=LOG_FORMAT, datefmt="%Y-%m-%d %H:%M:%S"
+    ))
+    logger.addHandler(handler)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH -next 08/14] kddv/cmd: Add command to run testcases
  2023-11-18 10:40 [PATCH -next 00/14] Implement a ligth weight device driver test framework Zhang Xiaoxu
                   ` (6 preceding siblings ...)
  2023-11-18 10:40 ` [PATCH -next 07/14] kddv/cmd: Add command to create/remove mockup device Zhang Xiaoxu
@ 2023-11-18 10:40 ` Zhang Xiaoxu
  2023-11-18 10:40 ` [PATCH -next 09/14] kddv/core: Add test support for SPI driver Zhang Xiaoxu
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Zhang Xiaoxu @ 2023-11-18 10:40 UTC (permalink / raw)
  To: zhangxiaoxu5, weiyongjun1, linux-kernel, broonie, rostedt, mingo,
	frowand.list, linux-spi

From: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>

Add command kddv.cmds.test, which can help to run the testcase
use the python unittest framework.
  $ python3 -m kddv.cmd.test
  or
  $ python3 -m kddv.cmd.test kddv.tests.hwmon.test_max31722

Also enable fault inject test or enable kmemleak check. Usage:

Enable fault inject test:
  $ python3 -m kddv.cmds.test --fault-inject=all
  or
  $ python3 -m kddv.cmds.test --fault-inject=all \
  kddv.tests.hwmon.test_max31722.TestMax31722.test_device_probe

Enable kmemleak check:
  $ python3 -m kddv.cmds.test --kmemleak

Both fault inject and kmemleak will increase the time times.

Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>
---
 tools/testing/kddv/kddv/cmds/test.py | 75 ++++++++++++++++++++++++++++
 1 file changed, 75 insertions(+)
 create mode 100755 tools/testing/kddv/kddv/cmds/test.py

diff --git a/tools/testing/kddv/kddv/cmds/test.py b/tools/testing/kddv/kddv/cmds/test.py
new file mode 100755
index 000000000000..9be346983286
--- /dev/null
+++ b/tools/testing/kddv/kddv/cmds/test.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+import os
+import sys
+import fnmatch
+import argparse
+import unittest
+
+from kddv.core.ddunit import DriverTest
+from kddv.core.environ import environ
+from . import utils
+
+ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+def _list_suite(suite, tfilter=None):
+    for t in suite:
+        if isinstance(t, unittest.TestSuite):
+            _list_suite(t, tfilter)
+        elif isinstance(t, DriverTest):
+            id = t.id()
+            if tfilter and not any(fnmatch.fnmatch(id, f) for f in tfilter):
+                continue
+            print(f"kddv.tests.{id}")
+        else:
+            return None
+
+def list_suite(args):
+    args.filter = [f"*{f}*" for f in args.filter]
+    loader = unittest.defaultTestLoader
+    suites = loader.discover(os.path.join(ROOT_DIR, 'tests'))
+    _list_suite(suites, args.filter)
+    return 0
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+
+    parser.add_argument(
+        "--log-level", type=str, default=None,
+        choices=utils.LOG_LEVELS, help="Log Level"
+    )
+    parser.add_argument(
+        "--list", action='store_true', default=False,
+        help="List test cases",
+    )
+    parser.add_argument(
+        "--kmemleak", action='store_true', default=False,
+        help="Enable kmemeleak check",
+    )
+    parser.add_argument(
+        "--fault-inject", type=str, default=None,
+        choices=["slab", "page", "all"],
+        help="Enable fault inject features",
+    )
+    parser.add_argument("--filter", nargs="+", default=[],)
+
+    args, argv = parser.parse_known_args(sys.argv)
+    if args.log_level:
+        utils.setup_logger(args.log_level)
+    if args.list:
+        return list_suite(args)
+    if args.kmemleak:
+        environ.enable_kmemleak()
+    if args.fault_inject:
+        environ.enable_fault_inject(args.fault_inject)
+
+    unittest.main(verbosity=2, module=None, argv=argv)
+
+if __name__ == "__main__":
+    main()
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH -next 09/14] kddv/core: Add test support for SPI driver
  2023-11-18 10:40 [PATCH -next 00/14] Implement a ligth weight device driver test framework Zhang Xiaoxu
                   ` (7 preceding siblings ...)
  2023-11-18 10:40 ` [PATCH -next 08/14] kddv/cmd: Add command to run testcases Zhang Xiaoxu
@ 2023-11-18 10:40 ` Zhang Xiaoxu
  2023-11-18 10:40 ` [PATCH -next 10/14] kddv/tests: Add support for testing hwmon driver Zhang Xiaoxu
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Zhang Xiaoxu @ 2023-11-18 10:40 UTC (permalink / raw)
  To: zhangxiaoxu5, weiyongjun1, linux-kernel, broonie, rostedt, mingo,
	frowand.list, linux-spi

From: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>

The spi driver test case should inherit the 'SPIDriverTest'
and config the driver as below:

  MAX31722_REG_CFG = 0x00
  MAX31722_RESOLUTION_12BIT = 0x06

  class TestMax31722(SPIDriverTest):
      name = 'max31722'

      @property
      def regbytes(self):
          return 1

      @property
      def valbytes(self):
          return 1

      @property
      def configs(self):
          return { CFG_REG_MASK: 0x7f }

      def test_device_probe(self):
          with self.assertRaisesFault():
              with self.device() as dev:
                  self.assertRegEqual(MAX31722_REG_CFG,
                                      MAX31722_RESOLUTION_12BIT)

The kddv can auto load the 'spi-xfer-r{regbytes}v{valbytes}.o' bpf
program as the mock device chipsets.

Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: Zhang Xiaoxu <zhangxiaoxu@huawei.com>
---
 tools/testing/kddv/kddv/Makefile              |  5 +
 tools/testing/kddv/kddv/core/__init__.py      |  6 ++
 .../testing/kddv/kddv/core/buses/__init__.py  | 13 +++
 tools/testing/kddv/kddv/core/buses/spi.py     | 74 ++++++++++++++
 tools/testing/kddv/kddv/core/mockup.py        |  8 ++
 tools/testing/kddv/kddv/data/Makefile         | 21 ++++
 tools/testing/kddv/kddv/data/bpf/Makefile     | 22 +++++
 .../kddv/kddv/data/bpf/spi/spi-xfer-base.h    | 99 +++++++++++++++++++
 .../kddv/kddv/data/bpf/spi/spi-xfer-r1v1.c    | 51 ++++++++++
 .../kddv/kddv/data/bpf/spi/spi-xfer-r1v2.c    | 51 ++++++++++
 .../kddv/kddv/data/bpf/spi/spi-xfer-r1v3.c    | 86 ++++++++++++++++
 .../kddv/kddv/data/bpf/spi/spi-xfer-r2v1.c    | 51 ++++++++++
 .../kddv/kddv/data/bpf/spi/spi-xfer-r2v2.c    | 51 ++++++++++
 .../kddv/kddv/data/bpf/spi/spi-xfer-r3v1.c    | 52 ++++++++++
 .../kddv/kddv/data/bpf/spi/spi-xfer-r4v4.c    | 89 +++++++++++++++++
 15 files changed, 679 insertions(+)
 create mode 100755 tools/testing/kddv/kddv/core/buses/__init__.py
 create mode 100755 tools/testing/kddv/kddv/core/buses/spi.py
 create mode 100644 tools/testing/kddv/kddv/data/Makefile
 create mode 100644 tools/testing/kddv/kddv/data/bpf/Makefile
 create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-base.h
 create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r1v1.c
 create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r1v2.c
 create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r1v3.c
 create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r2v1.c
 create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r2v2.c
 create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r3v1.c
 create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r4v4.c

diff --git a/tools/testing/kddv/kddv/Makefile b/tools/testing/kddv/kddv/Makefile
index a5c91fcb0e9a..b7a12069e443 100644
--- a/tools/testing/kddv/kddv/Makefile
+++ b/tools/testing/kddv/kddv/Makefile
@@ -2,6 +2,7 @@
 include ../../../scripts/Makefile.include
 
 INSTALL ?= install
+SUBDIRS := data
 
 all:
 	@for SUBDIR in $(SUBDIRS); do		\
@@ -15,6 +16,10 @@ install:
 	cp -rf cmds/ $(INSTALL_PATH)
 	cp -rf tests/ $(INSTALL_PATH)
 
+	@for SUBDIR in $(SUBDIRS); do		\
+		$(MAKE) INSTALL_PATH=$(INSTALL_PATH)/$$SUBDIR -C $$SUBDIR install; \
+	done;
+
 clean:
 	@for SUBDIR in $(SUBDIRS); do		\
 		$(MAKE) -C $$SUBDIR clean;	\
diff --git a/tools/testing/kddv/kddv/core/__init__.py b/tools/testing/kddv/kddv/core/__init__.py
index 45a35c909e86..601464c0e9ed 100755
--- a/tools/testing/kddv/kddv/core/__init__.py
+++ b/tools/testing/kddv/kddv/core/__init__.py
@@ -5,3 +5,9 @@
 #
 # Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
 # Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+__all__ = [
+    'SPIDriverTest',
+]
+
+from .buses import SPIDriverTest
diff --git a/tools/testing/kddv/kddv/core/buses/__init__.py b/tools/testing/kddv/kddv/core/buses/__init__.py
new file mode 100755
index 000000000000..1cbd4bed181a
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/buses/__init__.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+__all__ = [
+    'SPIDriverTest',
+]
+
+from .spi import SPIDriverTest
diff --git a/tools/testing/kddv/kddv/core/buses/spi.py b/tools/testing/kddv/kddv/core/buses/spi.py
new file mode 100755
index 000000000000..13c96de5b7bd
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/buses/spi.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+import logging
+
+from pathlib import Path
+
+from ..ddunit import DriverTest
+from ..device import Device
+from ..mockup import Mockup
+
+logger = logging.getLogger(__name__)
+
+class SPIDriverTest(DriverTest):
+    bus = 'spi'
+
+    @property
+    def bpf(self):
+        return f'spi-xfer-r{self.regbytes}v{self.valbytes}'
+
+class SPIDevice(Device):
+    bus = 'spi'
+
+    @property
+    def device_id(self):
+        return f"{self.bus}{self.busid}.{self.addr}"
+
+SPI_MASTER_PATH = '/sys/class/spi_master/spi0'
+
+class SPIMockup(Mockup):
+    bus = 'spi'
+    host = Path('/sys/kernel/config/spi-mockup/spi0')
+    live = Path('/sys/kernel/config/spi-mockup/spi0/live')
+
+    def setup(self):
+        logger.debug('setup')
+        if not self.host.exists():
+            self.host.mkdir()
+
+        self.live.write_text('true')
+
+    def teardown(self):
+        logger.debug('spi mockup teardown')
+        self.live.write_text('false')
+        self.host.rmdir()
+
+    @property
+    def device_id(self):
+        return f"{self.devid} {self.addr}"
+
+    def create_device(self):
+        logger.debug(f'new device {self.devid} to spi bus')
+        dev = Path(f'/sys/kernel/config/spi-mockup/spi0/targets/{self.devid}')
+        if not dev.exists():
+            dev.mkdir()
+        device_id = Path(f'/sys/kernel/config/spi-mockup/spi0/targets/{self.devid}/device_id')
+        device_id.write_text(self.devid)
+
+        device_live = Path(f'/sys/kernel/config/spi-mockup/spi0/targets/{self.devid}/live')
+        device_live.write_text('true')
+
+    def remove_device(self):
+        logger.debug(f'delete device {self.devid} from spi bus')
+        logger.debug(f'delete device {self.devid} to spi bus')
+        device_live = Path(f'/sys/kernel/config/spi-mockup/spi0/targets/{self.devid}/live')
+        device_live.write_text('false')
+        dev = Path(f'/sys/kernel/config/spi-mockup/spi0/targets/{self.devid}')
+        dev.rmdir()
+
diff --git a/tools/testing/kddv/kddv/core/mockup.py b/tools/testing/kddv/kddv/core/mockup.py
index 8bea2db97232..cf23dd711ca0 100755
--- a/tools/testing/kddv/kddv/core/mockup.py
+++ b/tools/testing/kddv/kddv/core/mockup.py
@@ -101,7 +101,14 @@ class Mockup(object):
     def remove_device(self):
         pass
 
+    def setup(self):
+        pass
+
+    def teardown(self):
+        pass
+
     def load(self):
+        self.setup()
         self.load_bpf()
         self.load_regmaps()
         self.load_configs()
@@ -110,6 +117,7 @@ class Mockup(object):
     def unload(self):
         self.remove_device()
         self.unload_bpf()
+        self.teardown()
 
     def bpf_map_name(self):
         bpf_name = re.sub("-", "_", self.bpf)
diff --git a/tools/testing/kddv/kddv/data/Makefile b/tools/testing/kddv/kddv/data/Makefile
new file mode 100644
index 000000000000..0e82984b99a9
--- /dev/null
+++ b/tools/testing/kddv/kddv/data/Makefile
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0
+include ../../../../scripts/Makefile.include
+
+SUBDIRS := bpf
+
+all:
+	@for SUBDIR in $(SUBDIRS); do \
+		$(MAKE) -C $$SUBDIR;\
+	done;
+
+install:
+	@for SUBDIR in $(SUBDIRS); do \
+		$(MAKE) INSTALL_PATH=$(INSTALL_PATH)/$$SUBDIR -C $$SUBDIR install;\
+	done;
+
+clean:
+	@for SUBDIR in $(SUBDIRS); do \
+		$(MAKE) -C $$SUBDIR clean;\
+	done;
+
+.PHONY: all install clean
diff --git a/tools/testing/kddv/kddv/data/bpf/Makefile b/tools/testing/kddv/kddv/data/bpf/Makefile
new file mode 100644
index 000000000000..f693f8da1034
--- /dev/null
+++ b/tools/testing/kddv/kddv/data/bpf/Makefile
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0
+include ../../../../../scripts/Makefile.include
+
+CC	= clang
+INSTALL ?= install
+
+bpf-objs-y := $(patsubst %.c,%.o,$(wildcard */*.c))
+
+CFLAGS += -I../../../../../bpf/bpftool/ -I../../../../../bpf/bpftool/libbpf/include
+CFLAGS += -Iinclude -Ispi
+
+all: $(bpf-objs-y)
+
+%.o: %.c
+	$(CC) -target bpf -Wall -O2 $(CFLAGS) -g -c $< -o $@
+
+install:
+	$(INSTALL) -m 0755 -d $(INSTALL_PATH)
+	$(INSTALL) $(bpf-objs-y) $(INSTALL_PATH)
+
+clean:
+	rm -rf $(bpf-objs-y)
diff --git a/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-base.h b/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-base.h
new file mode 100644
index 000000000000..8a33e6049dbc
--- /dev/null
+++ b/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-base.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * The spi xfer helpers for bpf program
+ *
+ * Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+ */
+
+#ifndef __SPI_XFER_BASE_
+#define __SPI_XFER_BASE_
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_core_read.h>
+
+int spi_xfer_read_u8(struct spi_msg_ctx *msg, unsigned int len,
+		     void *map, unsigned int reg, int offset)
+{
+	unsigned int i, key = reg;
+	u8 *value;
+
+	for (i = offset; i < len && i < sizeof(msg->data); i++, key++) {
+		value = bpf_map_lookup_elem(map, &key);
+		if (!value) {
+			bpf_printk("key 0x%x not exists", key);
+			return -1;
+		}
+
+		msg->data[i] = *value;
+
+		bpf_printk("SPI R8 [0x%x]=0x%x", key, msg->data[i]);
+	}
+
+	return 0;
+}
+
+int spi_xfer_write_u8(struct spi_msg_ctx *msg, unsigned int len,
+		      void *map, unsigned int reg, int offset)
+{
+	unsigned int i, key = reg;
+	u8 value;
+
+	for (i = offset; i < len && i < sizeof(msg->data); i++, key++) {
+		value = msg->data[i];
+
+		if (bpf_map_update_elem(map, &key, &value, BPF_EXIST)) {
+			bpf_printk("key 0x%x not exists", key);
+			return -1;
+		}
+
+		bpf_printk("SPI W8 [0x%x]=0x%x [%u/%u]", key, value, i, len);
+	}
+
+	return 0;
+}
+
+int spi_xfer_read_u16(struct spi_msg_ctx *msg, unsigned int len,
+		      void *map, unsigned int reg, int offset)
+{
+	unsigned int i, key = reg;
+	u16 *value;
+
+	for (i = offset; i < len && i < sizeof(msg->data) - 1; i += 2, key++) {
+		value = bpf_map_lookup_elem(map, &key);
+		if (!value) {
+			bpf_printk("key 0x%x not exists", key);
+			return -1;
+		}
+
+		msg->data[i + 0] = *value >> 8;
+		msg->data[i + 1] = *value & 0xff;
+
+		bpf_printk("SPI R16 [0x%x]=0x%x [%u/%u]", key, *value, i, len);
+	}
+
+	return 0;
+}
+
+int spi_xfer_write_u16(struct spi_msg_ctx *msg, unsigned int len,
+		       void *map, unsigned int reg, int offset)
+{
+	unsigned int i, key = reg;
+	u16 value;
+
+	for (i = offset; i < len && i < sizeof(msg->data) - 1; i += 2, key++) {
+		value = msg->data[i];
+		value = (value << 8) | msg->data[i + 1];
+
+		if (bpf_map_update_elem(map, &key, &value, BPF_EXIST)) {
+			bpf_printk("key 0x%x not exists", key);
+			return -1;
+		}
+
+		bpf_printk("SPI W16 [0x%x]=0x%x [%u/%u]", key, value, i, len);
+	}
+
+	return 0;
+}
+
+#endif
diff --git a/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r1v1.c b/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r1v1.c
new file mode 100644
index 000000000000..3e7252207e23
--- /dev/null
+++ b/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r1v1.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+#include <errno.h>
+
+#include "bpf-xfer-conf.h"
+#include "spi-xfer-base.h"
+
+#define CHIP_REGS_SIZE	0x100
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, CHIP_REGS_SIZE);
+	__type(key, __u32);
+	__type(value, __u8);
+} regs_spi_xfer_r1v1 SEC(".maps");
+
+static unsigned int chip_reg;
+
+static int spi_xfer_read(struct spi_msg_ctx *msg, unsigned int len)
+{
+	return spi_xfer_read_u8(msg, len, &regs_spi_xfer_r1v1, chip_reg, 0);
+}
+
+static int spi_xfer_write(struct spi_msg_ctx *msg, unsigned int len)
+{
+	chip_reg = bpf_xfer_reg_u8(msg->data[0]);
+	return spi_xfer_write_u8(msg, len, &regs_spi_xfer_r1v1, chip_reg, 1);
+}
+
+SEC("raw_tp.w/spi_transfer_writeable")
+int BPF_PROG(spi_xfer_r1v1, struct spi_msg_ctx *msg, u8 chip, unsigned int len)
+{
+	if (bpf_xfer_should_fault()) {
+		msg->ret = -EIO;
+		return 0;
+	}
+
+	if (msg->tx_nbits)
+		msg->ret = spi_xfer_write(msg, len);
+	else if (msg->rx_nbits)
+		msg->ret = spi_xfer_read(msg, len);
+
+	return 0;
+}
+
+char LICENSE[] SEC("license") = "GPL";
diff --git a/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r1v2.c b/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r1v2.c
new file mode 100644
index 000000000000..ef9ff13a573d
--- /dev/null
+++ b/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r1v2.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+#include <errno.h>
+
+#include "bpf-xfer-conf.h"
+#include "spi-xfer-base.h"
+
+#define CHIP_REGS_SIZE	0x100
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, CHIP_REGS_SIZE);
+	__type(key, __u32);
+	__type(value, __u16);
+} regs_spi_xfer_r1v2 SEC(".maps");
+
+static unsigned int chip_reg;
+
+static int spi_xfer_read(struct spi_msg_ctx *msg, unsigned int len)
+{
+	return spi_xfer_read_u16(msg, len, &regs_spi_xfer_r1v2, chip_reg, 0);
+}
+
+static int spi_xfer_write(struct spi_msg_ctx *msg, unsigned int len)
+{
+	chip_reg = bpf_xfer_reg_u8(msg->data[0]);
+	return spi_xfer_write_u16(msg, len, &regs_spi_xfer_r1v2, chip_reg, 1);
+}
+
+SEC("raw_tp.w/spi_transfer_writeable")
+int BPF_PROG(spi_xfer_r1v2, struct spi_msg_ctx *msg, u8 chip, unsigned int len)
+{
+	if (bpf_xfer_should_fault()) {
+		msg->ret = -EIO;
+		return 0;
+	}
+
+	if (msg->tx_nbits)
+		msg->ret = spi_xfer_write(msg, len);
+	else if (msg->rx_nbits)
+		msg->ret = spi_xfer_read(msg, len);
+
+	return 0;
+}
+
+char LICENSE[] SEC("license") = "GPL";
diff --git a/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r1v3.c b/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r1v3.c
new file mode 100644
index 000000000000..b720115d16d2
--- /dev/null
+++ b/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r1v3.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+#include <errno.h>
+
+#include "bpf-xfer-conf.h"
+#include "spi-xfer-base.h"
+
+#define CHIP_REGS_SIZE	0x100
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, CHIP_REGS_SIZE);
+	__type(key, __u32);
+	__type(value, __u32);
+} regs_spi_xfer_r1v3 SEC(".maps");
+
+static unsigned int chip_reg;
+
+static int spi_xfer_read(struct spi_msg_ctx *msg, unsigned int len)
+{
+	unsigned int i, key = chip_reg;
+	u32 *value;
+
+	for (i = 0; i < len && i < sizeof(msg->data) - 2; i += 3, key++) {
+		value = bpf_map_lookup_elem(&regs_spi_xfer_r1v3, &key);
+		if (!value) {
+			bpf_printk("key 0x%x not exists", key);
+			return -EINVAL;
+		}
+
+		msg->data[i + 0] = (*value >> 16) & 0xff;
+		msg->data[i + 1] = (*value >> 8) & 0xff;
+		msg->data[i + 2] = *value & 0xff;
+
+		bpf_printk("SPI R24 [0x%x]=0x%x [%u/%u]", key, *value, i, len);
+	}
+
+	return 0;
+}
+
+static int spi_xfer_write(struct spi_msg_ctx *msg, unsigned int len)
+{
+	unsigned int i, key;
+	u32 value;
+
+	chip_reg = bpf_xfer_reg_u8(msg->data[0]);
+	key = chip_reg;
+
+	for (i = 1; i < len && i < sizeof(msg->data) - 2; i += 3, key++) {
+		value = msg->data[i];
+		value = (value << 8) | msg->data[i + 1];
+		value = (value << 8) | msg->data[i + 2];
+
+		if (bpf_map_update_elem(&regs_spi_xfer_r1v3, &key, &value, BPF_EXIST)) {
+			bpf_printk("key 0x%x not exists", key);
+			return -EINVAL;
+		}
+
+		bpf_printk("SPI W24 [0x%x]=0x%x", key, value);
+	}
+
+	return 0;
+}
+
+SEC("raw_tp.w/spi_transfer_writeable")
+int BPF_PROG(spi_xfer_r1v3, struct spi_msg_ctx *msg, u8 chip, unsigned int len)
+{
+	if (bpf_xfer_should_fault()) {
+		msg->ret = -EIO;
+		return 0;
+	}
+
+	if (msg->tx_nbits)
+		msg->ret = spi_xfer_write(msg, len);
+	else if (msg->rx_nbits)
+		msg->ret = spi_xfer_read(msg, len);
+
+	return 0;
+}
+
+char LICENSE[] SEC("license") = "GPL";
diff --git a/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r2v1.c b/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r2v1.c
new file mode 100644
index 000000000000..c1e7942be327
--- /dev/null
+++ b/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r2v1.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+#include <errno.h>
+
+#include "bpf-xfer-conf.h"
+#include "spi-xfer-base.h"
+
+#define CHIP_REGS_SIZE	0x1000
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, CHIP_REGS_SIZE);
+	__type(key, __u32);
+	__type(value, __u8);
+} regs_spi_xfer_r2v1 SEC(".maps");
+
+static unsigned int chip_reg;
+
+static int spi_xfer_read(struct spi_msg_ctx *msg, unsigned int len)
+{
+	return spi_xfer_read_u8(msg, len, &regs_spi_xfer_r2v1, chip_reg, 0);
+}
+
+static int spi_xfer_write(struct spi_msg_ctx *msg, unsigned int len)
+{
+	chip_reg = bpf_xfer_reg_u16(msg->data[0] << 8 | msg->data[1]);
+	return spi_xfer_write_u8(msg, len, &regs_spi_xfer_r2v1, chip_reg, 2);
+}
+
+SEC("raw_tp.w/spi_transfer_writeable")
+int BPF_PROG(spi_xfer_r2v1, struct spi_msg_ctx *msg, u8 chip, unsigned int len)
+{
+	if (bpf_xfer_should_fault()) {
+		msg->ret = -EIO;
+		return 0;
+	}
+
+	if (msg->tx_nbits)
+		msg->ret = spi_xfer_write(msg, len);
+	else if (msg->rx_nbits)
+		msg->ret = spi_xfer_read(msg, len);
+
+	return 0;
+}
+
+char LICENSE[] SEC("license") = "GPL";
diff --git a/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r2v2.c b/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r2v2.c
new file mode 100644
index 000000000000..4076722824ba
--- /dev/null
+++ b/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r2v2.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+#include <errno.h>
+
+#include "bpf-xfer-conf.h"
+#include "spi-xfer-base.h"
+
+#define CHIP_REGS_SIZE	0x1000
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, CHIP_REGS_SIZE);
+	__type(key, __u32);
+	__type(value, __u16);
+} regs_spi_xfer_r2v2 SEC(".maps");
+
+static unsigned int chip_reg;
+
+static int spi_xfer_read(struct spi_msg_ctx *msg, unsigned int len)
+{
+	return spi_xfer_read_u16(msg, len, &regs_spi_xfer_r2v2, chip_reg, 0);
+}
+
+static int spi_xfer_write(struct spi_msg_ctx *msg, unsigned int len)
+{
+	chip_reg = bpf_xfer_reg_u16(msg->data[0] << 8 | msg->data[1]);
+	return spi_xfer_write_u16(msg, len, &regs_spi_xfer_r2v2, chip_reg, 2);
+}
+
+SEC("raw_tp.w/spi_transfer_writeable")
+int BPF_PROG(spi_xfer_r2v2, struct spi_msg_ctx *msg, u8 chip, unsigned int len)
+{
+	if (bpf_xfer_should_fault()) {
+		msg->ret = -EIO;
+		return 0;
+	}
+
+	if (msg->tx_nbits)
+		msg->ret = spi_xfer_write(msg, len);
+	else if (msg->rx_nbits)
+		msg->ret = spi_xfer_read(msg, len);
+
+	return 0;
+}
+
+char LICENSE[] SEC("license") = "GPL";
diff --git a/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r3v1.c b/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r3v1.c
new file mode 100644
index 000000000000..7f056b8a7bdb
--- /dev/null
+++ b/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r3v1.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+#include <errno.h>
+
+#include "bpf-xfer-conf.h"
+#include "spi-xfer-base.h"
+
+#define CHIP_REGS_SIZE	0x1000
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, CHIP_REGS_SIZE);
+	__type(key, __u32);
+	__type(value, __u8);
+} regs_spi_xfer_r3v1 SEC(".maps");
+
+static unsigned int chip_reg;
+
+static int spi_xfer_read(struct spi_msg_ctx *msg, unsigned int len)
+{
+	return spi_xfer_read_u8(msg, len, &regs_spi_xfer_r3v1, chip_reg, 0);
+}
+
+static int spi_xfer_write(struct spi_msg_ctx *msg, unsigned int len)
+{
+	chip_reg = bpf_xfer_reg_u24((msg->data[0] << 16) | (msg->data[1] << 8) |
+				    msg->data[2]);
+	return spi_xfer_write_u8(msg, len, &regs_spi_xfer_r3v1, chip_reg, 3);
+}
+
+SEC("raw_tp.w/spi_transfer_writeable")
+int BPF_PROG(spi_xfer_r3v1, struct spi_msg_ctx *msg, u8 chip, unsigned int len)
+{
+	if (bpf_xfer_should_fault()) {
+		msg->ret = -EIO;
+		return 0;
+	}
+
+	if (msg->tx_nbits)
+		msg->ret = spi_xfer_write(msg, len);
+	else if (msg->rx_nbits)
+		msg->ret = spi_xfer_read(msg, len);
+
+	return 0;
+}
+
+char LICENSE[] SEC("license") = "GPL";
diff --git a/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r4v4.c b/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r4v4.c
new file mode 100644
index 000000000000..70f839a2222a
--- /dev/null
+++ b/tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r4v4.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+#include <errno.h>
+
+#include "bpf-xfer-conf.h"
+#include "spi-xfer-base.h"
+
+#define CHIP_REGS_SIZE	0x8000
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, CHIP_REGS_SIZE);
+	__type(key, __u32);
+	__type(value, __u32);
+} regs_spi_xfer_r4v4 SEC(".maps");
+
+static unsigned int chip_reg;
+
+static int spi_xfer_read(struct spi_msg_ctx *msg, unsigned int len)
+{
+	unsigned int i, key = chip_reg;
+	u32 *value;
+
+	for (i = 0; i < len && i < sizeof(msg->data) - 3; i += 4, key++) {
+		value = bpf_map_lookup_elem(&regs_spi_xfer_r4v4, &key);
+		if (!value) {
+			bpf_printk("key 0x%x not exists", key);
+			return -EINVAL;
+		}
+
+		msg->data[i + 0] = (*value >> 24) & 0xff;
+		msg->data[i + 1] = (*value >> 16) & 0xff;
+		msg->data[i + 2] = (*value >> 8) & 0xff;
+		msg->data[i + 3] = *value & 0xff;
+
+		bpf_printk("SPI R32 [0x%x]=0x%x [%u/%u]", key, *value, i, len);
+	}
+
+	return 0;
+}
+
+static int spi_xfer_write(struct spi_msg_ctx *msg, unsigned int len)
+{
+	unsigned int i, key;
+	u32 value;
+
+	key = bpf_xfer_reg_u32((msg->data[0] << 24) | (msg->data[1] << 16) |
+			       (msg->data[2] << 8) | msg->data[3]);
+	chip_reg = key;
+
+	for (i = 4; i < len && i < sizeof(msg->data) - 3; i += 4, key++) {
+		value = msg->data[i];
+		value = (value << 8) | msg->data[i + 1];
+		value = (value << 8) | msg->data[i + 2];
+		value = (value << 8) | msg->data[i + 3];
+
+		if (bpf_map_update_elem(&regs_spi_xfer_r4v4, &key, &value, 0)) {
+			bpf_printk("key 0x%x not exists", key);
+			return -EINVAL;
+		}
+
+		bpf_printk("SPI W32 [0x%x]=0x%x [%u/%u]", key, value, i, len);
+	}
+
+	return 0;
+}
+
+SEC("raw_tp.w/spi_transfer_writeable")
+int BPF_PROG(spi_xfer_r4v4, struct spi_msg_ctx *msg, u8 chip, unsigned int len)
+{
+	if (bpf_xfer_should_fault()) {
+		msg->ret = -EIO;
+		return 0;
+	}
+
+	if (msg->tx_nbits)
+		msg->ret = spi_xfer_write(msg, len);
+	else if (msg->rx_nbits)
+		msg->ret = spi_xfer_read(msg, len);
+
+	return 0;
+}
+
+char LICENSE[] SEC("license") = "GPL";
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH -next 10/14] kddv/tests: Add support for testing hwmon driver
  2023-11-18 10:40 [PATCH -next 00/14] Implement a ligth weight device driver test framework Zhang Xiaoxu
                   ` (8 preceding siblings ...)
  2023-11-18 10:40 ` [PATCH -next 09/14] kddv/core: Add test support for SPI driver Zhang Xiaoxu
@ 2023-11-18 10:40 ` Zhang Xiaoxu
  2023-11-18 10:40 ` [PATCH -next 11/14] kddv/tests/hwmon: Add test cases for max31722 driver Zhang Xiaoxu
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Zhang Xiaoxu @ 2023-11-18 10:40 UTC (permalink / raw)
  To: zhangxiaoxu5, weiyongjun1, linux-kernel, broonie, rostedt, mingo,
	frowand.list, linux-spi

From: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>

This implement some helper function for hwmon device,
hwmon driver test case can inherit 'HwMonDriver' to simplify code.

Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>
---
 .../testing/kddv/kddv/tests/hwmon/__init__.py | 425 ++++++++++++++++++
 1 file changed, 425 insertions(+)
 create mode 100755 tools/testing/kddv/kddv/tests/hwmon/__init__.py

diff --git a/tools/testing/kddv/kddv/tests/hwmon/__init__.py b/tools/testing/kddv/kddv/tests/hwmon/__init__.py
new file mode 100755
index 000000000000..0b2efedbb02e
--- /dev/null
+++ b/tools/testing/kddv/kddv/tests/hwmon/__init__.py
@@ -0,0 +1,425 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+import logging
+from pathlib import Path
+
+logger = logging.getLogger(__name__)
+
+class HwMon:
+    def __init__(self, path: Path) -> None:
+        hwmon = next(path.glob("hwmon/hwmon*")).name
+        self.cdev = Path(f"/sys/class/hwmon/{hwmon}")
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, *_):
+        pass
+
+    def read_attr(self, attr):
+        rpath = self.cdev / attr
+        logger.debug(f"read from file {rpath}")
+        return rpath.read_text().rstrip()
+
+    def write_attr(self, attr, value):
+        wpath = self.cdev / attr
+        logger.debug(f"write '{value}' to file {wpath}")
+        wpath.write_text(value)
+
+class HwMonDriver:
+    def hwmon_read_attr(self, dev, attr):
+        with HwMon(dev.path) as hwmon:
+            return int(hwmon.read_attr(attr))
+
+    def hwmon_write_attr(self, dev, attr, value):
+        with HwMon(dev.path) as hwmon:
+            return hwmon.write_attr(attr, str(value))
+
+    def hwmon_read_attr_str(self, dev, attr):
+        with HwMon(dev.path) as hwmon:
+            return hwmon.read_attr(attr)
+
+    def hwmon_write_attr_str(self, dev, attr, value):
+        with HwMon(dev.path) as hwmon:
+            return hwmon.write_attr(attr, value)
+
+    def hwmon_read_temp_label(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_label')
+
+    def hwmon_read_temp_input(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_input')
+
+    def hwmon_write_temp_input(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_input', value)
+
+    def hwmon_read_temp_lcrit(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_lcrit')
+
+    def hwmon_write_temp_lcrit(self, dev, value, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_lcrit', value)
+
+    def hwmon_read_temp_lcrit_hyst(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_lcrit_hyst')
+
+    def hwmon_write_temp_lcrit_hyst(self, dev, value, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_lcrit_hyst', value)
+
+    def hwmon_read_temp_min(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_min')
+
+    def hwmon_write_temp_min(self, dev, value, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_min', value)
+
+    def hwmon_read_temp_min_hyst(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_min_hyst')
+
+    def hwmon_write_temp_min_hyst(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_min_hyst', value)
+
+    def hwmon_read_temp_max(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_max')
+
+    def hwmon_write_temp_max(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_max', value)
+
+    def hwmon_read_temp_max_hyst(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_max_hyst')
+
+    def hwmon_write_temp_max_hyst(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_max_hyst', value)
+
+    def hwmon_read_temp_crit(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_crit')
+
+    def hwmon_write_temp_crit(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_crit', value)
+
+    def hwmon_read_temp_crit_hyst(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_crit_hyst')
+
+    def hwmon_write_temp_crit_hyst(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_crit_hyst', value)
+
+    def hwmon_read_temp_emergency(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_emergency')
+
+    def hwmon_write_temp_emergency(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_emergency', value)
+
+    def hwmon_read_temp_emergency_hyst(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_emergency_hyst')
+
+    def hwmon_write_temp_emergency_hyst(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_emergency_hyst', value)
+
+    def hwmon_read_temp_alarm(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_alarm')
+
+    def hwmon_write_temp_alarm(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_alarm', value)
+
+    def hwmon_read_temp_lcrit_alarm(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_lcrit_alarm')
+
+    def hwmon_write_temp_lcrit_alarm(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_lcrit_alarm', value)
+
+    def hwmon_read_temp_min_alarm(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_min_alarm')
+
+    def hwmon_write_temp_min_alarm(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_min_alarm', value)
+
+    def hwmon_read_temp_max_alarm(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_max_alarm')
+
+    def hwmon_write_temp_max_alarm(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_max_alarm', value)
+
+    def hwmon_read_temp_crit_alarm(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_crit_alarm')
+
+    def hwmon_write_temp_crit_alarm(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_crit_alarm', value)
+
+    def hwmon_read_temp_emergency_alarm(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_emergency_alarm')
+
+    def hwmon_write_temp_emergency_alarm(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_emergency_alarm', value)
+
+    def hwmon_read_temp_fault(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_fault')
+
+    def hwmon_write_temp_fault(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_fault', value)
+
+    def hwmon_read_temp_offset(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_offset')
+
+    def hwmon_write_temp_offset(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_offset', value)
+
+    def hwmon_read_temp_label(self, dev, chan=0):
+        return self.hwmon_read_attr_str(dev, f'temp{chan+1}_label')
+
+    def hwmon_write_temp_label(self, dev, value, chan=0):
+        return self.hwmon_write_attr_str(dev, f'temp{chan+1}_label', value)
+
+    def hwmon_read_temp_lowest(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_lowest')
+
+    def hwmon_write_temp_lowest(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_lowest', value)
+
+    def hwmon_read_temp_highest(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_highest')
+
+    def hwmon_write_temp_highest(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_highest', value)
+
+    def hwmon_read_temp_reset_history(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_reset_history')
+
+    def hwmon_write_temp_reset_history(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_reset_history', value)
+
+    def hwmon_read_temp_rated_min(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_rated_min')
+
+    def hwmon_write_temp_rated_min(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_rated_min', value)
+
+    def hwmon_read_temp_rated_max(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'temp{chan+1}_rated_max')
+
+    def hwmon_write_temp_rated_max(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'temp{chan+1}_rated_max', value)
+
+    def hwmon_read_fan_enable(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'fan{chan+1}_enable')
+
+    def hwmon_write_fan_enable(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'fan{chan+1}_enable', value)
+
+    def hwmon_read_fan_input(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'fan{chan+1}_input')
+
+    def hwmon_write_fan_input(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'fan{chan+1}_input', value)
+
+    def hwmon_read_fan_fault(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'fan{chan+1}_fault')
+
+    def hwmon_write_fan_fault(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'fan{chan+1}_fault', value)
+
+    def hwmon_read_pwm_input(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'pwm{chan+1}')
+
+    def hwmon_write_pwm_input(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'pwm{chan+1}', value)
+
+    def hwmon_read_pwm_enable(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'pwm{chan+1}_enable')
+
+    def hwmon_write_pwm_enable(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'pwm{chan+1}_enable', value)
+
+    def hwmon_read_pwm_mode(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'pwm{chan+1}_mode')
+
+    def hwmon_write_pwm_mode(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'pwm{chan+1}_mode', value)
+
+    def hwmon_read_pwm_freq(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'pwm{chan+1}_freq')
+
+    def hwmon_write_pwm_freq(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'pwm{chan+1}_freq', value)
+
+    def hwmon_read_pwm_auto_channels_temp(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'pwm{chan+1}_auto_channels_temp')
+
+    def hwmon_write_pwm_auto_channels_temp(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'pwm{chan+1}_auto_channels_temp', value)
+
+    def hwmon_read_in_label(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'in{chan+1}_label')
+
+    def hwmon_read_in_input(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'in{chan+1}_input')
+
+    def hwmon_write_in_input(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'in{chan+1}_input', value)
+
+    def hwmon_read_in_min(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'in{chan+1}_min')
+
+    def hwmon_write_in_min(self, dev, value, chan=0):
+        return self.hwmon_read_attr(dev, f'in{chan+1}_min', value)
+
+    def hwmon_read_in_min_alarm(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'in{chan+1}_min_alarm')
+
+    def hwmon_write_in_min_alarm(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'in{chan+1}_min_alarm', value)
+
+    def hwmon_read_in_max(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'in{chan+1}_max')
+
+    def hwmon_write_in_max(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'in{chan+1}_max', value)
+
+    def hwmon_read_in_max_alarm(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'in{chan+1}_max_alarm')
+
+    def hwmon_write_in_max_alarm(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'in{chan+1}_max_alarm', value)
+
+    def hwmon_read_in_crit(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'in{chan+1}_crit')
+
+    def hwmon_write_in_crit(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'in{chan+1}_crit', value)
+
+    def hwmon_read_in_crit_alarm(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'in{chan+1}_crit_alarm')
+
+    def hwmon_write_in_crit_alarm(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'in{chan+1}_crit_alarm', value)
+
+    def hwmon_read_in_lcrit(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'in{chan+1}_lcrit')
+
+    def hwmon_write_in_lcrit(self, dev, value, chan=0):
+        return self.hwmon_read_attr(dev, f'in{chan+1}_lcrit', value)
+
+    def hwmon_read_in_lcrit_alarm(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'in{chan+1}_lcrit_alarm')
+
+    def hwmon_write_in_lcrit_alarm(self, dev, value, chan=0):
+        return self.hwmon_read_attr(dev, f'in{chan+1}_lcrit_alarm', value)
+
+    def hwmon_read_in_rated_min(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'in{chan+1}_rated_min')
+
+    def hwmon_write_in_rated_min(self, dev, value, chan=0):
+        return self.hwmon_read_attr(dev, f'in{chan+1}_rated_min', value)
+
+    def hwmon_read_in_rated_max(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'in{chan+1}_rated_max')
+
+    def hwmon_write_in_rated_max(self, dev, value, chan=0):
+        return self.hwmon_read_attr(dev, f'in{chan+1}_rated_max', value)
+
+    def hwmon_read_curr_label(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'curr{chan+1}_label')
+
+    def hwmon_read_curr_input(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'curr{chan+1}_input')
+
+    def hwmon_write_curr_input(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'curr{chan+1}_input', value)
+
+    def hwmon_read_curr_max(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'curr{chan+1}_max')
+
+    def hwmon_write_curr_max(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'curr{chan+1}_max', value)
+
+    def hwmon_read_curr_max_alarm(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'curr{chan+1}_max_alarm')
+
+    def hwmon_write_curr_max_alarm(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'curr{chan+1}_max_alarm', value)
+
+    def hwmon_read_curr_crit(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'curr{chan+1}_crit')
+
+    def hwmon_write_curr_crit(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'curr{chan+1}_crit', value)
+
+    def hwmon_read_curr_crit_alarm(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'curr{chan+1}_crit_alarm')
+
+    def hwmon_write_curr_crit_alarm(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'curr{chan+1}_crit_alarm', value)
+
+    def hwmon_read_curr_lcrit(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'curr{chan+1}_lcrit')
+
+    def hwmon_write_curr_lcrit(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'curr{chan+1}_lcrit', value)
+
+    def hwmon_read_curr_lcrit_alarm(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'curr{chan+1}_lcrit_alarm')
+
+    def hwmon_write_curr_lcrit_alarm(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'curr{chan+1}_lcrit_alarm', value)
+
+    def hwmon_read_curr_rated_max(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'curr{chan+1}_rated_max')
+
+    def hwmon_write_curr_rated_max(self, dev, value, chan=0):
+        return self.hwmon_read_attr(dev, f'curr{chan+1}_rated_max', value)
+
+    def hwmon_read_power_input(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'power{chan+1}_input')
+
+    def hwmon_write_power_input(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'power{chan+1}_input', value)
+
+    def hwmon_read_power_alarm(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'power{chan+1}_alarm')
+
+    def hwmon_write_power_alarm(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'power{chan+1}_alarm', value)
+
+    def hwmon_read_power_cap(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'power{chan+1}_cap')
+
+    def hwmon_write_power_cap(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'power{chan+1}_cap', value)
+
+    def hwmon_read_power_cap_alarm(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'power{chan+1}_cap_alarm')
+
+    def hwmon_write_power_cap_alarm(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'power{chan+1}_cap_alarm', value)
+
+    def hwmon_read_power_max(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'power{chan+1}_max')
+
+    def hwmon_write_power_max(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'power{chan+1}_max', value)
+
+    def hwmon_read_power_max_alarm(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'power{chan+1}_max_alarm')
+
+    def hwmon_write_power_max_alarm(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'power{chan+1}_max_alarm', value)
+
+    def hwmon_read_power_crit(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'power{chan+1}_crit')
+
+    def hwmon_write_power_crit(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'power{chan+1}_crit', value)
+
+    def hwmon_read_power_crit_alarm(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'power{chan+1}_crit_alarm')
+
+    def hwmon_write_power_crit_alarm(self, dev, value, chan=0):
+        return self.hwmon_write_attr(dev, f'power{chan+1}_crit_alarm', value)
+
+    def hwmon_read_power_rated_max(self, dev, chan=0):
+        return self.hwmon_read_attr(dev, f'power{chan+1}_rated_max')
+
+    def hwmon_write_power_rated_max(self, dev, value, chan=0):
+        return self.hwmon_read_attr(dev, f'power{chan+1}_rated_max', value)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH -next 11/14] kddv/tests/hwmon: Add test cases for max31722 driver
  2023-11-18 10:40 [PATCH -next 00/14] Implement a ligth weight device driver test framework Zhang Xiaoxu
                   ` (9 preceding siblings ...)
  2023-11-18 10:40 ` [PATCH -next 10/14] kddv/tests: Add support for testing hwmon driver Zhang Xiaoxu
@ 2023-11-18 10:40 ` Zhang Xiaoxu
  2023-11-18 10:40 ` [PATCH -next 12/14] kddv/tests: Add support for testing mtd driver Zhang Xiaoxu
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Zhang Xiaoxu @ 2023-11-18 10:40 UTC (permalink / raw)
  To: zhangxiaoxu5, weiyongjun1, linux-kernel, broonie, rostedt, mingo,
	frowand.list, linux-spi

From: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>

This add test case for max31722 hwmon driver, test for
the device loading and read temperature from the device.

Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>
---
 .../kddv/kddv/tests/hwmon/test_max31722.py    | 40 +++++++++++++++++++
 1 file changed, 40 insertions(+)
 create mode 100755 tools/testing/kddv/kddv/tests/hwmon/test_max31722.py

diff --git a/tools/testing/kddv/kddv/tests/hwmon/test_max31722.py b/tools/testing/kddv/kddv/tests/hwmon/test_max31722.py
new file mode 100755
index 000000000000..70223eb0ef7b
--- /dev/null
+++ b/tools/testing/kddv/kddv/tests/hwmon/test_max31722.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+from kddv.core import SPIDriverTest
+from kddv.core.consts import CFG_REG_MASK
+from . import HwMonDriver
+import errno
+
+MAX31722_REG_CFG = 0x00
+MAX31722_REG_TEMP_LSB = 0x01
+MAX31722_MODE_CONTINUOUS = 0x00
+MAX31722_RESOLUTION_12BIT = 0x06
+
+class TestMax31722(SPIDriverTest, HwMonDriver):
+    name = 'max31722'
+
+    @property
+    def configs(self):
+        return { CFG_REG_MASK: 0x7f }
+
+    def test_device_probe(self):
+        with self.assertRaisesFault():
+            with self.device() as dev:
+                self.assertRegEqual(MAX31722_REG_CFG, MAX31722_RESOLUTION_12BIT)
+
+    def test_read_temp_input(self):
+        with self.device() as dev:
+            self.write_regs(MAX31722_REG_TEMP_LSB, [0x12, 0x34])
+            temp = self.hwmon_read_temp_input(dev)
+            self.assertEqual(temp, int(0x3412 * 125 / 32))
+
+            self.trigger_io_fault()
+            with self.assertRaises(OSError) as cm:
+                self.hwmon_read_temp_input(dev)
+            self.assertEqual(cm.exception.errno, errno.EIO)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH -next 12/14] kddv/tests: Add support for testing mtd driver
  2023-11-18 10:40 [PATCH -next 00/14] Implement a ligth weight device driver test framework Zhang Xiaoxu
                   ` (10 preceding siblings ...)
  2023-11-18 10:40 ` [PATCH -next 11/14] kddv/tests/hwmon: Add test cases for max31722 driver Zhang Xiaoxu
@ 2023-11-18 10:40 ` Zhang Xiaoxu
  2023-11-18 10:40 ` [PATCH -next 13/14] kddv/tests/mtd: Add test cases for mchp23k256 driver Zhang Xiaoxu
  2023-11-18 10:40 ` [PATCH -next 14/14] kddv: Add document for kddv Zhang Xiaoxu
  13 siblings, 0 replies; 15+ messages in thread
From: Zhang Xiaoxu @ 2023-11-18 10:40 UTC (permalink / raw)
  To: zhangxiaoxu5, weiyongjun1, linux-kernel, broonie, rostedt, mingo,
	frowand.list, linux-spi

From: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>

This implement some helper function for mtd device,
mtd driver test case can inherit 'MTDDriver' to simplify code.

Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: Zhang Xiaoxu <zhangxiaoxu@huawei.com>
---
 tools/testing/kddv/kddv/tests/mtd/__init__.py | 63 +++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 tools/testing/kddv/kddv/tests/mtd/__init__.py

diff --git a/tools/testing/kddv/kddv/tests/mtd/__init__.py b/tools/testing/kddv/kddv/tests/mtd/__init__.py
new file mode 100644
index 000000000000..4defd92deb05
--- /dev/null
+++ b/tools/testing/kddv/kddv/tests/mtd/__init__.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+import os
+import logging
+
+from pathlib import Path
+
+logger = logging.getLogger(__name__)
+
+class MTD(object):
+    def __init__(self, path):
+        mtd = next(path.glob("mtd/mtd*")).name
+        self.cdev = Path(f"/sys/class/mtd/{mtd}")
+        self.rdev = f"/dev/{mtd}"
+
+    def read_bytes(self, len, offset = 0):
+        with open(self.rdev, "rb") as dev:
+            if offset:
+                dev.seek(offset)
+            return dev.read(len)
+
+    def write_bytes(self, data, offset = 0):
+        with open(self.rdev, "wb") as dev:
+            if offset:
+                dev.seek(offset)
+            dev.write(data)
+
+    def read_attr(self, attr):
+        path = self.cdev / attr
+        logger.debug(f"read from {path}")
+        if not os.path.exists(path):
+            return f"attr '{attr}' not exists"
+        return path.read_text().rstrip()
+
+    def write_attr(self, attr, val):
+        path = self.cdev / attr
+        if not os.path.exists(path):
+            return f"attr '{attr}' not exists"
+        logger.debug(f"write '{val}' to {path}")
+        return path.write_bytes(val.encode())
+
+class MTDDriver(object):
+    def mtd_read_attr(self, dev, attr):
+        mtddev = MTD(dev.path)
+        return mtddev.read_attr(attr)
+
+    def mtd_write_attr(self, dev, attr, val):
+        mtddev = MTD(dev.path)
+        return mtddev.write_attr(attr)
+
+    def mtd_read_bytes(self, dev, len):
+        mtddev = MTD(dev.path)
+        return mtddev.read_bytes(len)
+
+    def mtd_write_bytes(self, dev, data):
+        mtddev = MTD(dev.path)
+        return mtddev.write_bytes(data)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH -next 13/14] kddv/tests/mtd: Add test cases for mchp23k256 driver
  2023-11-18 10:40 [PATCH -next 00/14] Implement a ligth weight device driver test framework Zhang Xiaoxu
                   ` (11 preceding siblings ...)
  2023-11-18 10:40 ` [PATCH -next 12/14] kddv/tests: Add support for testing mtd driver Zhang Xiaoxu
@ 2023-11-18 10:40 ` Zhang Xiaoxu
  2023-11-18 10:40 ` [PATCH -next 14/14] kddv: Add document for kddv Zhang Xiaoxu
  13 siblings, 0 replies; 15+ messages in thread
From: Zhang Xiaoxu @ 2023-11-18 10:40 UTC (permalink / raw)
  To: zhangxiaoxu5, weiyongjun1, linux-kernel, broonie, rostedt, mingo,
	frowand.list, linux-spi

From: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>

This add test case for mchp23k256 mtd driver, test for
create mchp23k256 device, default size of the device and
read/write data from device.

Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: Zhang Xiaoxu <zhangxiaoxu@huawei.com>
---
 .../kddv/kddv/data/bpf/mtd/mtd-mchp23k256.c   | 72 +++++++++++++++++++
 .../kddv/kddv/tests/mtd/test_mchp23k256.py    | 41 +++++++++++
 2 files changed, 113 insertions(+)
 create mode 100644 tools/testing/kddv/kddv/data/bpf/mtd/mtd-mchp23k256.c
 create mode 100755 tools/testing/kddv/kddv/tests/mtd/test_mchp23k256.py

diff --git a/tools/testing/kddv/kddv/data/bpf/mtd/mtd-mchp23k256.c b/tools/testing/kddv/kddv/data/bpf/mtd/mtd-mchp23k256.c
new file mode 100644
index 000000000000..b1aa8a25edc0
--- /dev/null
+++ b/tools/testing/kddv/kddv/data/bpf/mtd/mtd-mchp23k256.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2022-2023 Huawei Technologies Co., Ltd */
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <errno.h>
+
+#include "spi-xfer-base.h"
+
+#define MCHP23K256_CMD_WRITE_STATUS	0x01
+#define MCHP23K256_CMD_WRITE		0x02
+#define MCHP23K256_CMD_READ		0x03
+
+#define CHIP_REGS_SIZE			0x20000
+
+#define MAX_CMD_SIZE			4
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, CHIP_REGS_SIZE);
+	__type(key, __u32);
+	__type(value, __u8);
+} regs_mtd_mchp23k256 SEC(".maps");
+
+static unsigned int chip_reg;
+
+static int spi_transfer_read(struct spi_msg_ctx *msg, unsigned int len)
+{
+	return spi_xfer_read_u8(msg, len, &regs_mtd_mchp23k256, chip_reg, 0);
+}
+
+static int spi_transfer_write(struct spi_msg_ctx *msg, unsigned int len)
+{
+	u8 opcode = msg->data[0];
+	int i;
+
+	switch (opcode) {
+	case MCHP23K256_CMD_READ:
+	case MCHP23K256_CMD_WRITE:
+		if (len < 2)
+			return -EINVAL;
+
+		chip_reg = 0;
+		for (i = 0; i < MAX_CMD_SIZE && i < len - 1; i++)
+			chip_reg = (chip_reg << 8) + msg->data[1 + i];
+
+		return 0;
+	case MCHP23K256_CMD_WRITE_STATUS:
+		// ignore write status
+		return 0;
+	default:
+		break;
+	}
+
+	return spi_xfer_write_u8(msg, len, &regs_mtd_mchp23k256, chip_reg, 0);
+}
+
+SEC("raw_tp.w/spi_transfer_writeable")
+int BPF_PROG(mtd_mchp23k256, struct spi_msg_ctx *msg, u8 chip, unsigned int len)
+{
+	int ret = 0;
+
+	if (msg->tx_nbits)
+		ret = spi_transfer_write(msg, len);
+	else if (msg->rx_nbits)
+		ret = spi_transfer_read(msg, len);
+
+	return ret;
+}
+
+char LICENSE[] SEC("license") = "GPL";
diff --git a/tools/testing/kddv/kddv/tests/mtd/test_mchp23k256.py b/tools/testing/kddv/kddv/tests/mtd/test_mchp23k256.py
new file mode 100755
index 000000000000..eefad9d70483
--- /dev/null
+++ b/tools/testing/kddv/kddv/tests/mtd/test_mchp23k256.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <weiyongjun1@huawei.com>
+
+from kddv.core import SPIDriverTest
+from . import MTDDriver
+
+MCHP23K256_TEST_DATA = [0x78] * 16
+
+class TestMCHP23K256(SPIDriverTest, MTDDriver):
+    name = "mchp23k256"
+
+    @property
+    def bpf(self):
+        return f"mtd-{self.name}"
+
+    def test_device_probe(self):
+        with self.assertRaisesFault():
+            with self.device() as _:
+                pass
+
+    def test_device_size(self):
+        with self.device() as dev:
+            size = self.mtd_read_attr(dev, 'size')
+            self.assertEqual(size, '32768')
+
+    def test_read_data(self):
+        with self.device() as dev:
+            self.write_regs(0x00, MCHP23K256_TEST_DATA)
+            data = self.mtd_read_bytes(dev, 16)
+            self.assertEqual(data, bytes(MCHP23K256_TEST_DATA))
+
+    def test_write_data(self):
+        with self.device() as dev:
+            self.write_regs(0x00, [0] * 16)
+            self.mtd_write_bytes(dev, bytes(MCHP23K256_TEST_DATA))
+            self.assertRegsEqual(0x00, MCHP23K256_TEST_DATA)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH -next 14/14] kddv: Add document for kddv
  2023-11-18 10:40 [PATCH -next 00/14] Implement a ligth weight device driver test framework Zhang Xiaoxu
                   ` (12 preceding siblings ...)
  2023-11-18 10:40 ` [PATCH -next 13/14] kddv/tests/mtd: Add test cases for mchp23k256 driver Zhang Xiaoxu
@ 2023-11-18 10:40 ` Zhang Xiaoxu
  13 siblings, 0 replies; 15+ messages in thread
From: Zhang Xiaoxu @ 2023-11-18 10:40 UTC (permalink / raw)
  To: zhangxiaoxu5, weiyongjun1, linux-kernel, broonie, rostedt, mingo,
	frowand.list, linux-spi

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=y, Size: 7968 bytes --]

From: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>

This add documentation for the kddv, include the design principles,
how to run the testcases, and how to contribute new testcases

Signed-off-by: Zhang Xiaoxu <zhangxiaoxu@huawei.com>
---
 Documentation/dev-tools/index.rst |   1 +
 Documentation/dev-tools/kddv.rst  | 183 ++++++++++++++++++++++++++++++
 2 files changed, 184 insertions(+)
 create mode 100755 Documentation/dev-tools/kddv.rst

diff --git a/Documentation/dev-tools/index.rst b/Documentation/dev-tools/index.rst
index 6b0663075dc0..ef8203db8fd7 100644
--- a/Documentation/dev-tools/index.rst
+++ b/Documentation/dev-tools/index.rst
@@ -34,6 +34,7 @@ Documentation/dev-tools/testing-overview.rst
    kselftest
    kunit/index
    ktap
+   kddv
 
 
 .. only::  subproject and html
diff --git a/Documentation/dev-tools/kddv.rst b/Documentation/dev-tools/kddv.rst
new file mode 100755
index 000000000000..9a031448312a
--- /dev/null
+++ b/Documentation/dev-tools/kddv.rst
@@ -0,0 +1,183 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. Copyright(c) 2022 Huawei Technologies Co., Ltd.
+
+Kernel Device Driver Verification (KDDV)
+========================================
+
+The kernel code contains a large amount of driver code which
+depends on specific hardware. As a result, the test of this part
+of code is difficult. Due to resource limitations, common detection
+methods such as KASAN are difficult to deploy. Therefore, a test
+method independent of physical hardware is required.
+
+Generally, the driver reads related information from the physical
+hardware by calling a specific function. Then the driver code can
+be tested by mocking the specific function to simulate physical
+hardware behavior by returning virtual values.
+
+With the development of eBPF, it is possible to mock a specific
+function based on the eBPF TracePoint mechanism, and then use
+eBPF to simulate physical hardware.
+
+Based on the python unittests framework, KDDV mounts devices to
+virtual buses and simulates hardware behaviors through the ebpf
+program to test specific driver functions. In addition, KDDV supports
+fault injection to simulate exceptions, verify that the driver
+behaves properly.
+
+
+Design principles
+-----------------
+
+The driver can register these device to the mock bus, it can be interacts
+with bpf program, then the physical hardware can be mocked by bpf.
+
+::
+
+    __________________________________       _______________________________
+    |User Program                    |       | Linux kernel                |
+    |                                |       |                             |
+    |                                |       |                             |
+    |            _________________   | load  |    _____________      _____ |
+    |llvm -----> | eBPF bytecode |  -|-------|--> | Verifier  |      | M | |
+    |            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯   |       |    ¯¯¯¯¯¯¯¯¯¯¯¯¯      | O | |
+    |                                |       |                       | C | |
+    |                                |       |    _____________      | K | |
+    |                                |       |    |   eBPF    | <--> |   | |
+    |                                |       |    ¯¯¯¯¯¯¯¯¯¯¯¯¯      | B | |
+    |            _______________     | read  |    _____________      | U | |
+    |bpftool --> | Access Regs | ----|-------|--> | eBPF Maps |      | S | |
+    |            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯     | write |    ¯¯¯¯¯¯¯¯¯¯¯¯¯      ¯¯¯¯¯ |
+    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯       ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
+
+
+
+Running the KDDV
+----------------
+
+The kddv depends on the mock bus, should configure kernel as below::
+
+    CONFIG_SPI_MOCK=y
+    ...
+
+To build the tests::
+
+  $ make -C tools/testing/kddv/
+
+To install kddv in a user specified location::
+
+   $ make -C tools/testing/selftests install INSTALL_PATH=/some/other/path
+
+To run the tests::
+
+  $ cd /some/other/path
+  $ python3 -m kddv.cmds.test
+
+More useful info can be got from the help::
+
+  $ python3 -m kddv.cmds.test --help
+
+
+Contributing new tests
+----------------------
+
+Test file placement
+~~~~~~~~~~~~~~~~~~~
+
+The directory structure is as below::
+
+        tools/testing/kddv/kddv
+        ├── cmds  # Python unittests command line
+        ├── core  # General data structure of the kddv
+        ├── data  # Extra data, eg: bpf program
+        │   ├── bpf
+        │   │   ├── include
+        │   │   │   └── bpf-xfer-conf.h
+        │   │   ├── Makefile
+        │   │   ├── mtd
+        │   │   │   └── mtd-mchp23k256.c
+        │   │   └── spi
+        │   └── Makefile
+        ├── tests  # KDDV testcases
+        │   ├── __init__.py
+        │   ├── hwmon
+        │   │   ├── __init__.py
+        │   │   └── test_max31722.py
+        │   └── mtd
+        │       ├── __init__.py
+        │       └── test_mchp23k256.py
+        ├── __init__.py
+        └── Makefile
+
+If you want to add a new testcase to kddv, you should:
+  1. add the python unittests to the **tests** directory.
+  2. add the bpf program to the **data/bpf** directory.
+
+Basic example
+~~~~~~~~~~~~~
+
+The kddv based on python unittests, it provides a set of tools for write
+the testcase, here is a basic example for test some feature of mchp23k256
+mtd device::
+
+  from kddv.core import SPIDriverTest
+  from . import MTDDriver
+
+  MCHP23K256_TEST_DATA = [0x78] * 16
+
+  class TestMCHP23K256(SPIDriverTest, MTDDriver):
+      name = "mchp23k256"
+
+      @property
+      def bpf(self):
+          return f"mtd-{self.name}"
+
+      def test_device_probe(self):
+          with self.assertRaisesFault():
+              with self.device() as _:
+                  pass
+
+      def test_device_size(self):
+          with self.device() as dev:
+              size = self.mtd_read_attr(dev, 'size')
+              self.assertEqual(size, '32768')
+
+      def test_read_data(self):
+          with self.device() as dev:
+              self.write_regs(0x00, MCHP23K256_TEST_DATA)
+              data = self.mtd_read_bytes(dev, 16)
+              self.assertEqual(data, bytes(MCHP23K256_TEST_DATA))
+
+      def test_write_data(self):
+          with self.device() as dev:
+              self.write_regs(0x00, [0] * 16)
+              self.mtd_write_bytes(dev, bytes(MCHP23K256_TEST_DATA))
+              self.assertRegsEqual(0x00, MCHP23K256_TEST_DATA)
+
+**SPIDriverTest** provides the basic functions for SPI driver, such as
+loading the driver, read/write SPI registers.
+
+**MTDDriver** provides some basic function for MTD driver, such as get the
+mtd device info, read/write data from mtd device.
+
+Since the mchp23k256 is a mtd driver based on SPI bus, so **TestMCHP23K256**
+inherited from **SPIDriverTest** and **MTDDriver**.
+
+There are 4 testcases for mchp23k256 basic function:
+  1. driver and device can be loading successfully;
+  2. the device size should be 32k as the default;
+  3. read data from device, the data should be equal with the regs;
+  4. write data to device, the regs should be equal with the data;
+
+The above script produces an output that looks like this::
+
+  $ python3 -m kddv.cmds.test kddv.tests.mtd.test_mchp23k256
+  test_device_probe (kddv.tests.mtd.test_mchp23k256.TestMCHP23K256) ... ok
+  test_device_size (kddv.tests.mtd.test_mchp23k256.TestMCHP23K256) ... ok
+  test_read_data (kddv.tests.mtd.test_mchp23k256.TestMCHP23K256) ... ok
+  test_write_data (kddv.tests.mtd.test_mchp23k256.TestMCHP23K256) ... ok
+
+  ----------------------------------------------------------------------
+  Ran 4 tests in 36.100s
+
+  OK
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2023-11-18 10:41 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-18 10:40 [PATCH -next 00/14] Implement a ligth weight device driver test framework Zhang Xiaoxu
2023-11-18 10:40 ` [PATCH -next 01/14] kddv/core: " Zhang Xiaoxu
2023-11-18 10:40 ` [PATCH -next 02/14] kddv/core: Allow test case config bpf program Zhang Xiaoxu
2023-11-18 10:40 ` [PATCH -next 03/14] kddv/core: Add io fault support to " Zhang Xiaoxu
2023-11-18 10:40 ` [PATCH -next 04/14] kddv/core: Check kmsg before return from test case Zhang Xiaoxu
2023-11-18 10:40 ` [PATCH -next 05/14] kddv/core: Support kernel memory leak detector Zhang Xiaoxu
2023-11-18 10:40 ` [PATCH -next 06/14] kddv/core: Add page and slab fault inject support Zhang Xiaoxu
2023-11-18 10:40 ` [PATCH -next 07/14] kddv/cmd: Add command to create/remove mockup device Zhang Xiaoxu
2023-11-18 10:40 ` [PATCH -next 08/14] kddv/cmd: Add command to run testcases Zhang Xiaoxu
2023-11-18 10:40 ` [PATCH -next 09/14] kddv/core: Add test support for SPI driver Zhang Xiaoxu
2023-11-18 10:40 ` [PATCH -next 10/14] kddv/tests: Add support for testing hwmon driver Zhang Xiaoxu
2023-11-18 10:40 ` [PATCH -next 11/14] kddv/tests/hwmon: Add test cases for max31722 driver Zhang Xiaoxu
2023-11-18 10:40 ` [PATCH -next 12/14] kddv/tests: Add support for testing mtd driver Zhang Xiaoxu
2023-11-18 10:40 ` [PATCH -next 13/14] kddv/tests/mtd: Add test cases for mchp23k256 driver Zhang Xiaoxu
2023-11-18 10:40 ` [PATCH -next 14/14] kddv: Add document for kddv Zhang Xiaoxu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).