diff mbox series

[5/5] arm/oeqa: Introduce the fvp_devices test suite

Message ID 20230717185626.1793017-5-peter.hoyes@arm.com
State New
Headers show
Series [1/5] runfvp: Add missing conffile include | expand

Commit Message

Peter Hoyes July 17, 2023, 6:56 p.m. UTC
From: Peter Hoyes <Peter.Hoyes@arm.com>

The fvp_devices test suite can be used to verify the following
functionality at runtime, common to most FVPs:
 * CPU hotplug
 * virtio-net device presence and functionality
 * virtio-rng device presence and functionality
 * PL031 RTC device presence and functionality
 * SP805 watchdog device presence

The list of devices to be tested can be configured by a BSP using the
variable TEST_FVP_DEVICES.

Add this test suite for fvp-base and fvp-baser-aemv8r64.

Signed-off-by: Peter Hoyes <Peter.Hoyes@arm.com>
---
 .../conf/machine/fvp-baser-aemv8r64.conf      |   3 +-
 .../conf/machine/include/fvp-common.inc       |   3 +-
 .../lib/oeqa/runtime/cases/fvp_devices.py     | 130 ++++++++++++++++++
 3 files changed, 134 insertions(+), 2 deletions(-)
 create mode 100644 meta-arm/lib/oeqa/runtime/cases/fvp_devices.py
diff mbox series

Patch

diff --git a/meta-arm-bsp/conf/machine/fvp-baser-aemv8r64.conf b/meta-arm-bsp/conf/machine/fvp-baser-aemv8r64.conf
index 7dbc53a1..c0ac3d75 100644
--- a/meta-arm-bsp/conf/machine/fvp-baser-aemv8r64.conf
+++ b/meta-arm-bsp/conf/machine/fvp-baser-aemv8r64.conf
@@ -30,9 +30,10 @@  MACHINE_EXTRA_RRECOMMENDS += "ssh-pregen-hostkeys"
 
 # testimage configuration
 TEST_TARGET = "OEFVPTarget"
-TEST_SUITES:append = " fvp_boot"
+TEST_SUITES:append = " fvp_boot fvp_devices"
 TEST_TARGET_IP ?= "127.0.0.1:8022"
 TEST_SERVER_IP ?= "127.0.1.1"
+TEST_FVP_DEVICES ?= "rtc watchdog networking virtiorng cpu_hotplug"
 
 FVP_EXTRA_ARGS = "-a cluster0*=linux-system.axf"
 FVP_PROVIDER ?= "fvp-base-r-aem-native"
diff --git a/meta-arm-bsp/conf/machine/include/fvp-common.inc b/meta-arm-bsp/conf/machine/include/fvp-common.inc
index f80ac4c2..b76de3c4 100644
--- a/meta-arm-bsp/conf/machine/include/fvp-common.inc
+++ b/meta-arm-bsp/conf/machine/include/fvp-common.inc
@@ -24,7 +24,8 @@  MACHINE_EXTRA_RRECOMMENDS += "ssh-pregen-hostkeys"
 
 TEST_TARGET = "OEFVPTarget"
 TEST_TARGET_IP = "127.0.0.1:8022"
-TEST_SUITES:append = " fvp_boot"
+TEST_SUITES:append = " fvp_boot fvp_devices"
+TEST_FVP_DEVICES ?= "rtc watchdog networking virtiorng cpu_hotplug"
 
 FVP_PROVIDER ?= "fvp-base-a-aem-native"
 FVP_EXE ?= "FVP_Base_RevC-2xAEMvA"
diff --git a/meta-arm/lib/oeqa/runtime/cases/fvp_devices.py b/meta-arm/lib/oeqa/runtime/cases/fvp_devices.py
new file mode 100644
index 00000000..0246e76a
--- /dev/null
+++ b/meta-arm/lib/oeqa/runtime/cases/fvp_devices.py
@@ -0,0 +1,130 @@ 
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.data import skipIfNotInDataVar
+from oeqa.core.decorator.depends import OETestDepends
+
+
+class FvpDevicesTest(OERuntimeTestCase):
+    def run_cmd(self, cmd, check=True):
+        """
+        A wrapper around self.target.run, which:
+          * Fails the test on command failure by default
+          * Allows the "run" behavior to be overridden in sub-classes
+        """
+        (status, output) = self.target.run(cmd)
+        if status and check:
+            self.fail("Command '%s' returned non-zero exit "
+                      "status %d:\n%s" % (cmd, status, output))
+
+        return (status, output)
+
+    def check_devices(self, cls, min_count, search_drivers):
+        # Find all the devices of the specified class
+        cmd = f'find "/sys/class/{cls}" -type l -maxdepth 1'
+        _, output = self.run_cmd(cmd)
+
+        devices = output.split()
+        self.assertGreaterEqual(len(devices),
+                                min_count,
+                                msg='Device count is lower than expected')
+
+        # Assert that at least one of the devices uses at least one of the
+        # drivers
+        drivers = set()
+        for device in devices:
+            cmd = f'basename "$(readlink "{device}/device/driver")"'
+            _, output = self.run_cmd(cmd)
+            drivers.update(output.split())
+
+        self.assertTrue(drivers & set(search_drivers),
+                      msg='No device uses either of the drivers: ' +
+                        str(search_drivers))
+
+    def check_rng(self, hw_random, dev):
+        cmd = f'cat {hw_random} | grep {dev}'
+        self.run_cmd(cmd)
+
+    def set_cpu(self, cpu_num, flag):
+        # Issue echo command
+        self.run_cmd(
+            f'echo "{flag}" > "/sys/devices/system/cpu/cpu{cpu_num}/online"',
+            check = False,
+        )
+        _, output = self.run_cmd(
+            f'cat "/sys/devices/system/cpu/cpu{cpu_num}/online"'
+        )
+
+        return output == flag
+
+    def enable_cpu(self, cpu_num):
+        return self.set_cpu(cpu_num, "1")
+
+    def disable_cpu(self, cpu_num):
+        return self.set_cpu(cpu_num, "0")
+
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    @skipIfNotInDataVar('TEST_FVP_DEVICES', 'cpu_hotplug',
+                        'cpu_hotplug not included in BSP tests')
+    def test_cpu_hotplug(self):
+        _, cpus = self.run_cmd('find /sys/firmware/devicetree/base/cpus/'
+                               ' -name "cpu@*" -maxdepth 1 | wc -l')
+
+        try:
+            count_cpus = int(cpus)
+        except ValueError:
+            self.fail(f"Expected number of CPUs, but found this:\n{cpus}")
+
+        self.num_cpus = int(self.td.get('TEST_CPU_HOTPLUG_NUM_CPUS',
+                                        count_cpus))
+        try:
+            # Test that all cores are online
+            _, cpus = self.run_cmd('grep -c "processor" /proc/cpuinfo')
+            self.assertEqual(int(cpus), self.num_cpus)
+            # Don't try to disable here the only cpu present in the system.
+            if self.num_cpus > 1:
+                # Test that we can stop each core individually
+                for i in range(self.num_cpus):
+                    self.assertTrue(self.disable_cpu(i))
+                    self.assertTrue(self.enable_cpu(i))
+
+            # Test that we cannot disable all cores
+            for i in range(self.num_cpus - 1):
+                self.assertTrue(self.disable_cpu(i))
+            # Disabling last core should trigger an error
+            self.assertFalse(self.disable_cpu(self.num_cpus - 1))
+        finally:
+            # Ensure all CPUs are re-enabled
+            for i in range(self.num_cpus):
+                self.enable_cpu(i)
+
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    @skipIfNotInDataVar('TEST_FVP_DEVICES', 'rtc',
+                        'rtc device not included in BSP tests')
+    def test_rtc(self):
+        self.check_devices("rtc", 1, ["rtc-pl031"])
+        self.run_cmd('hwclock')
+
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    @skipIfNotInDataVar('TEST_FVP_DEVICES', 'watchdog',
+                        'watchdog device not included in BSP tests')
+    def test_watchdog(self):
+        self.check_devices("watchdog", 1, ["sp805-wdt", "sbsa-gwdt"])
+
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    @skipIfNotInDataVar('TEST_FVP_DEVICES', 'networking',
+                        'networking device not included in BSP tests')
+    def test_networking(self):
+        self.check_devices("net", 2, ["virtio_net", "vif"])
+
+        # Check that outbound network connections work
+        self.run_cmd('wget -O /dev/null "https://www.arm.com"')
+
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    @skipIfNotInDataVar('TEST_FVP_DEVICES', 'virtiorng',
+                        'virtiorng device not included in BSP tests')
+    def test_virtiorng(self):
+        self.check_rng('/sys/devices/virtual/misc/hw_random/rng_available',
+                       'virtio_rng.0')
+        self.check_rng('/sys/devices/virtual/misc/hw_random/rng_current',
+                       'virtio_rng.0')
+
+        self.run_cmd('hexdump -n 32 /dev/hwrng')