Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,3 @@
TEST_PROGS := test_device_error_logs.py
include ../../lib.mk
@@ -0,0 +1,85 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (c) 2024 Collabora Ltd
#
# This test checks for the presence of error (or more critical) log messages
# coming from devices in the kernel log.
#
# One failed test case is reported for each device that has outputted error
# logs. Devices with no errors do not produce a passing test case to avoid
# polluting the results, therefore a successful run will list 0 tests run.
#
import glob
import os
import re
import sys
# Allow ksft module to be imported from different directory
this_dir = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(this_dir, "../../kselftest/"))
import ksft
kmsg = "/dev/kmsg"
RE_log = re.compile(
r"(?P<prefix>[0-9]+),(?P<sequence>[0-9]+),(?P<timestamp>[0-9]+),(?P<flag>[^;]*)(,[^;]*)*;(?P<message>.*)"
)
RE_tag = re.compile(r" (?P<key>[^=]+)=(?P<value>.*)")
PREFIX_ERROR = 3
logs = []
error_log_per_device = {}
def parse_kmsg():
current_log = {}
with open(kmsg) as f:
os.set_blocking(f.fileno(), False)
for line in f:
tag_line = RE_tag.match(line)
log_line = RE_log.match(line)
if log_line:
if current_log:
logs.append(current_log) # Save last log
current_log = {
"prefix": int(log_line.group("prefix")),
"sequence": int(log_line.group("sequence")),
"timestamp": int(log_line.group("timestamp")),
"flag": log_line.group("flag"),
"message": log_line.group("message"),
}
elif tag_line:
current_log[tag_line.group("key")] = tag_line.group("value")
def generate_per_device_error_log():
for log in logs:
if log.get("DEVICE") and log["prefix"] <= PREFIX_ERROR:
if not error_log_per_device.get(log["DEVICE"]):
error_log_per_device[log["DEVICE"]] = []
error_log_per_device[log["DEVICE"]].append(log)
parse_kmsg()
generate_per_device_error_log()
num_tests = len(error_log_per_device)
ksft.print_header()
ksft.set_plan(num_tests)
for device in error_log_per_device:
for log in error_log_per_device[device]:
ksft.print_msg(log["message"])
ksft.test_result_fail(device)
if num_tests == 0:
ksft.print_msg("No device error logs found")
ksft.finished()
@@ -0,0 +1,4 @@
TEST_PROGS := test_discoverable_devices.py
TEST_FILES := boards
include ../../lib.mk
@@ -0,0 +1,40 @@
# SPDX-License-Identifier: GPL-2.0
#
# This is the device definition for the XPS 13 9300.
# The filename "Dell Inc.,XPS 13 9300" was chosen following the format
# "Vendor,Product", where Vendor comes from
# /sys/devices/virtual/dmi/id/sys_vendor, and Product comes from
# /sys/devices/virtual/dmi/id/product_name.
#
# See google,spherion.yaml for more information.
#
- type: pci-controller
# This machine has a single PCI host controller so it's valid to not have any
# key to identify the controller. If it had more than one controller, the UID
# of the controller from ACPI could be used to distinguish as follows:
#acpi-uid: 0
devices:
- path: 14.0
type: usb-controller
usb-version: 2
devices:
- path: 9
name: camera
interfaces: [0, 1, 2, 3]
- path: 10
name: bluetooth
interfaces: [0, 1]
- path: 2.0
name: gpu
- path: 4.0
name: thermal
- path: 12.0
name: sensors
- path: 14.3
name: wifi
- path: 1d.0/0.0
name: ssd
- path: 1d.7/0.0
name: sdcard-reader
- path: 1f.3
name: audio
@@ -0,0 +1,54 @@
# SPDX-License-Identifier: GPL-2.0
#
# This is the device definition for the Google Spherion Chromebook.
# The filename "google,spherion" comes from the Devicetree compatible, so this
# file will be automatically used when the test is run on that machine.
#
# The top-level is a list of controllers, either for USB or PCI(e).
# Every controller needs to have a 'type' key set to either 'usb-controller' or
# 'pci-controller'.
# Every controller needs to be uniquely identified on the platform. To achieve
# this, several optional keys can be used:
# - dt-mmio: identify the MMIO address of the controller as defined in the
# Devicetree.
# - of-fullname-regex: regular expression to match against the OF_FULLNAME
# property. Useful when the controller's address is not unique across other
# sibling controllers. In this case, dt-mmio can't be used, and this property
# allows the matching to include parent nodes as well to make it unique.
# - usb-version: for USB controllers to differentiate between USB3 and USB2
# buses sharing the same controller.
# - acpi-uid: _UID property of the controller as supplied by the ACPI. Useful to
# distinguish between multiple PCI host controllers.
#
# The 'devices' key defines a list of devices that are accessible under that
# controller. A device might be a leaf device or another controller (see
# 'Dell Inc.,XPS 13 9300.yaml').
#
# The 'path' key is needed for every child device (that is, not top-level) to
# define how to reach this device from the parent controller. For USB devices it
# follows the format \d(.\d)* and denotes the port in the hub at each level in
# the USB topology. For PCI devices it follows the format \d.\d(/\d.\d)*
# denoting the device (identified by device-function pair) at each level in the
# PCI topology.
#
# The 'name' key is used in the leaf devices to name the device for clarity in
# the test output.
#
# For USB leaf devices, the 'interfaces' key should contain a list of the
# interfaces in that device that should be bound to a driver.
#
- type: usb-controller
dt-mmio: 11200000
usb-version: 2
devices:
- path: 1.4.1
interfaces: [0, 1]
name: camera
- path: 1.4.2
interfaces: [0, 1]
name: bluetooth
- type: pci-controller
dt-mmio: 11230000
devices:
- path: 0.0/0.0
name: wifi
@@ -0,0 +1,358 @@
#!/usr/bin/python3
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (c) 2023 Collabora Ltd
#
# This script tests for presence and driver binding of devices from discoverable
# buses (ie USB, PCI).
#
# The per-platform YAML file defining the devices to be tested is stored inside
# the boards/ directory and chosen based on DT compatible or DMI IDs (sys_vendor
# and product_name).
#
# See boards/google,spherion.yaml and boards/'Dell Inc.,XPS 13 9300.yaml' for
# the description and examples of the file structure and vocabulary.
#
import argparse
import glob
import os
import re
import sys
import yaml
# Allow ksft module to be imported from different directory
this_dir = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(this_dir, "../../kselftest/"))
import ksft
pci_controllers = []
usb_controllers = []
sysfs_usb_devices = "/sys/bus/usb/devices/"
def find_pci_controller_dirs():
sysfs_devices = "/sys/devices"
pci_controller_sysfs_dir = "pci[0-9a-f]{4}:[0-9a-f]{2}"
dir_regex = re.compile(pci_controller_sysfs_dir)
for path, dirs, _ in os.walk(sysfs_devices):
for d in dirs:
if dir_regex.match(d):
pci_controllers.append(os.path.join(path, d))
def find_usb_controller_dirs():
usb_controller_sysfs_dir = r"usb[\d]+"
dir_regex = re.compile(usb_controller_sysfs_dir)
for d in os.scandir(sysfs_usb_devices):
if dir_regex.match(d.name):
usb_controllers.append(os.path.realpath(d.path))
def get_dt_mmio(sysfs_dev_dir):
re_dt_mmio = re.compile("OF_FULLNAME=.*@([0-9a-f]+)")
dt_mmio = None
# PCI controllers' sysfs don't have an of_node, so have to read it from the
# parent
while not dt_mmio:
try:
with open(os.path.join(sysfs_dev_dir, "uevent")) as f:
dt_mmio = re_dt_mmio.search(f.read()).group(1)
return dt_mmio
except:
pass
sysfs_dev_dir = os.path.dirname(sysfs_dev_dir)
def get_of_fullname(sysfs_dev_dir):
re_of_fullname = re.compile("OF_FULLNAME=(.*)")
of_full_name = None
# PCI controllers' sysfs don't have an of_node, so have to read it from the
# parent
while not of_full_name:
try:
with open(os.path.join(sysfs_dev_dir, "uevent")) as f:
of_fullname = re_of_fullname.search(f.read()).group(1)
return of_fullname
except:
pass
sysfs_dev_dir = os.path.dirname(sysfs_dev_dir)
def get_acpi_uid(sysfs_dev_dir):
with open(os.path.join(sysfs_dev_dir, "firmware_node", "uid")) as f:
return f.read()
def get_usb_version(sysfs_dev_dir):
re_usb_version = re.compile(r"PRODUCT=.*/(\d)/.*")
with open(os.path.join(sysfs_dev_dir, "uevent")) as f:
return int(re_usb_version.search(f.read()).group(1))
def get_usb_busnum(sysfs_dev_dir):
re_busnum = re.compile("BUSNUM=(.*)")
with open(os.path.join(sysfs_dev_dir, "uevent")) as f:
return int(re_busnum.search(f.read()).group(1))
def find_controller_in_sysfs(controller, parent_sysfs=None):
if controller["type"] == "pci-controller":
controllers = pci_controllers
elif controller["type"] == "usb-controller":
controllers = usb_controllers
result_controllers = []
for c in controllers:
if parent_sysfs and parent_sysfs not in c:
continue
if controller.get("dt-mmio"):
if str(controller["dt-mmio"]) != get_dt_mmio(c):
continue
if controller.get("of-fullname-regex"):
re_of_fullname = re.compile(str(controller["of-fullname-regex"]))
if not re_of_fullname.match(get_of_fullname(c)):
continue
if controller.get("usb-version"):
if controller["usb-version"] != get_usb_version(c):
continue
if controller.get("acpi-uid"):
if controller["acpi-uid"] != get_acpi_uid(c):
continue
result_controllers.append(c)
return result_controllers
def is_controller(device):
return device.get("type") and "controller" in device.get("type")
def path_to_dir(parent_sysfs, dev_type, path):
if dev_type == "usb-device":
usb_dev_sysfs_fmt = "{}-{}"
busnum = get_usb_busnum(parent_sysfs)
dirname = os.path.join(
sysfs_usb_devices, usb_dev_sysfs_fmt.format(busnum, path)
)
return [os.path.realpath(dirname)]
else:
pci_dev_sysfs_fmt = "????:??:{}"
path_glob = ""
for dev_func in path.split("/"):
dev_func = dev_func.zfill(4)
path_glob = os.path.join(path_glob, pci_dev_sysfs_fmt.format(dev_func))
dir_list = glob.glob(os.path.join(parent_sysfs, path_glob))
return dir_list
def find_in_sysfs(device, parent_sysfs=None):
if parent_sysfs and device.get("path"):
pathdirs = path_to_dir(
parent_sysfs, device["meta"]["type"], str(device["path"])
)
if len(pathdirs) != 1:
# Early return to report error
return pathdirs
pathdir = pathdirs[0]
sysfs_path = os.path.join(parent_sysfs, pathdir)
else:
sysfs_path = parent_sysfs
if is_controller(device):
return find_controller_in_sysfs(device, sysfs_path)
else:
return [sysfs_path]
def check_driver_presence(sysfs_dir, current_node):
if current_node["meta"]["type"] == "usb-device":
usb_intf_fmt = "*-*:*.{}"
interfaces = []
for i in current_node["interfaces"]:
interfaces.append((i, usb_intf_fmt.format(i)))
for intf_num, intf_dir_fmt in interfaces:
test_name = f"{current_node['meta']['pathname']}.{intf_num}.driver"
intf_dirs = glob.glob(os.path.join(sysfs_dir, intf_dir_fmt))
if len(intf_dirs) != 1:
ksft.test_result_fail(test_name)
continue
intf_dir = intf_dirs[0]
driver_link = os.path.join(sysfs_dir, intf_dir, "driver")
ksft.test_result(os.path.isdir(driver_link), test_name)
else:
driver_link = os.path.join(sysfs_dir, "driver")
test_name = current_node["meta"]["pathname"] + ".driver"
ksft.test_result(os.path.isdir(driver_link), test_name)
def generate_pathname(device):
pathname = ""
if device.get("path"):
pathname = str(device["path"])
if device.get("type"):
dev_type = device["type"]
if device.get("usb-version"):
dev_type = dev_type.replace("usb", "usb" + str(device["usb-version"]))
if device.get("acpi-uid") is not None:
dev_type = dev_type.replace("pci", "pci" + str(device["acpi-uid"]))
pathname = pathname + "/" + dev_type
if device.get("dt-mmio"):
pathname += "@" + str(device["dt-mmio"])
if device.get("of-fullname-regex"):
pathname += "-" + str(device["of-fullname-regex"])
if device.get("name"):
pathname = pathname + "/" + device["name"]
return pathname
def fill_meta_keys(child, parent=None):
child["meta"] = {}
if parent:
child["meta"]["type"] = parent["type"].replace("controller", "device")
pathname = generate_pathname(child)
if parent:
pathname = parent["meta"]["pathname"] + "/" + pathname
child["meta"]["pathname"] = pathname
def parse_device_tree_node(current_node, parent_sysfs=None):
if not parent_sysfs:
fill_meta_keys(current_node)
sysfs_dirs = find_in_sysfs(current_node, parent_sysfs)
if len(sysfs_dirs) != 1:
if len(sysfs_dirs) == 0:
ksft.test_result_fail(
f"Couldn't find in sysfs: {current_node['meta']['pathname']}"
)
else:
ksft.test_result_fail(
f"Found multiple sysfs entries for {current_node['meta']['pathname']}: {sysfs_dirs}"
)
return
sysfs_dir = sysfs_dirs[0]
if not is_controller(current_node):
ksft.test_result(
os.path.exists(sysfs_dir), current_node["meta"]["pathname"] + ".device"
)
check_driver_presence(sysfs_dir, current_node)
else:
for child_device in current_node["devices"]:
fill_meta_keys(child_device, current_node)
parse_device_tree_node(child_device, sysfs_dir)
def count_tests(device_trees):
test_count = 0
def parse_node(device):
nonlocal test_count
if device.get("devices"):
for child in device["devices"]:
parse_node(child)
else:
if device.get("interfaces"):
test_count += len(device["interfaces"])
else:
test_count += 1
test_count += 1
for device_tree in device_trees:
parse_node(device_tree)
return test_count
def get_board_filenames():
filenames = []
platform_compatible_file = "/proc/device-tree/compatible"
if os.path.exists(platform_compatible_file):
with open(platform_compatible_file) as f:
for line in f:
filenames.extend(line.split("\0"))
else:
dmi_id_dir = "/sys/devices/virtual/dmi/id"
vendor_dmi_file = os.path.join(dmi_id_dir, "sys_vendor")
product_dmi_file = os.path.join(dmi_id_dir, "product_name")
with open(vendor_dmi_file) as f:
vendor = f.read().replace("\n", "")
with open(product_dmi_file) as f:
product = f.read().replace("\n", "")
filenames = [vendor + "," + product]
return filenames
def run_test(yaml_file):
ksft.print_msg(f"Using board file: {yaml_file}")
with open(yaml_file) as f:
device_trees = yaml.safe_load(f)
ksft.set_plan(count_tests(device_trees))
for device_tree in device_trees:
parse_device_tree_node(device_tree)
parser = argparse.ArgumentParser()
parser.add_argument(
"--boards-dir", default="boards", help="Directory containing the board YAML files"
)
args = parser.parse_args()
find_pci_controller_dirs()
find_usb_controller_dirs()
ksft.print_header()
if not os.path.exists(args.boards_dir):
ksft.print_msg(f"Boards directory '{args.boards_dir}' doesn't exist")
ksft.exit_fail()
board_file = ""
for board_filename in get_board_filenames():
full_board_filename = os.path.join(args.boards_dir, board_filename + ".yaml")
if os.path.exists(full_board_filename):
board_file = full_board_filename
break
if not board_file:
ksft.print_msg("No matching board file found")
ksft.exit_fail()
run_test(board_file)
ksft.finished()