From ec06f62df5340cd0a9466a532aa9806fb0e2e560 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Sat, 18 Feb 2023 12:04:04 +0000 Subject: [PATCH] drivers: Look up vendor and device names in PCI and USB IDs database (cherry picked from commit ca21ee4918cd7d4472bd875a495752a03a03fa87) --- .gitignore | 1 + configure.ac | 1 + drivers/Makefile.am | 6 +- drivers/drivers.ml | 31 +++++ drivers/hwdata.ml | 187 +++++++++++++++++++++++++++ drivers/hwdata.mli | 31 +++++ drivers/hwdata_config.ml.in | 26 ++++ drivers/hwdata_config.mli | 35 +++++ drivers/test-virt-drivers-windows.sh | 13 +- m4/guestfs-libraries.m4 | 3 + po/POTFILES-ml | 2 + 11 files changed, 333 insertions(+), 3 deletions(-) create mode 100644 drivers/hwdata.ml create mode 100644 drivers/hwdata.mli create mode 100644 drivers/hwdata_config.ml.in create mode 100644 drivers/hwdata_config.mli diff --git a/.gitignore b/.gitignore index b0ada2e3c..c0ca330a3 100644 --- a/.gitignore +++ b/.gitignore @@ -95,6 +95,7 @@ Makefile.in /customize/virt-customize /df/virt-df /drivers/.depend +/drivers/hwdata_config.ml /drivers/virt-drivers /diff/virt-diff /edit/virt-edit diff --git a/configure.ac b/configure.ac index 34c66b80e..e9fadcc9b 100644 --- a/configure.ac +++ b/configure.ac @@ -138,6 +138,7 @@ AC_CONFIG_FILES([Makefile df/Makefile diff/Makefile drivers/Makefile + drivers/hwdata_config.ml edit/Makefile format/Makefile get-kernel/Makefile diff --git a/drivers/Makefile.am b/drivers/Makefile.am index d27fc2e27..7e0ef659c 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -27,9 +27,13 @@ EXTRA_DIST = \ virt-drivers.pod SOURCES_MLI = \ - drivers.mli + drivers.mli \ + hwdata_config.mli \ + hwdata.mli SOURCES_ML = \ + hwdata_config.ml \ + hwdata.ml \ drivers.ml SOURCES_C = \ diff --git a/drivers/drivers.ml b/drivers/drivers.ml index 57cfb557c..f02165fa4 100644 --- a/drivers/drivers.ml +++ b/drivers/drivers.ml @@ -235,8 +235,14 @@ and windows_hardware_to_xml = function (Option.map (fun v -> ("class", sprintf "%06LX" v)) pci_class); List.may_push_back attrs (Option.map (fun v -> ("vendor", sprintf "%04LX" v)) pci_vendor); + let vendorname = get_pci_vendor pci_vendor in + List.may_push_back attrs + (Option.map (fun v -> "vendorname", v) vendorname); List.may_push_back attrs (Option.map (fun v -> ("device", sprintf "%04LX" v)) pci_device); + let devicename = get_pci_device pci_vendor pci_device in + List.may_push_back attrs + (Option.map (fun v -> "devicename", v) devicename); List.may_push_back attrs (Option.map (fun v -> ("subsystem", sprintf "%08LX" v)) pci_subsys); List.may_push_back attrs @@ -261,8 +267,14 @@ and windows_hardware_to_xml = function let attrs = ref [] in List.may_push_back attrs (Option.map (fun v -> ("vendor", sprintf "%04LX" v)) usb_vendor); + let vendorname = get_usb_vendor usb_vendor in + List.may_push_back attrs + (Option.map (fun v -> "vendorname", v) vendorname); List.may_push_back attrs (Option.map (fun v -> ("product", sprintf "%04LX" v)) usb_product); + let productname = get_usb_device usb_vendor usb_product in + List.may_push_back attrs + (Option.map (fun v -> "productname", v) productname); List.may_push_back attrs (Option.map (fun v -> ("revision", sprintf "%02LX" v)) usb_rev); List.may_push_back attrs @@ -272,6 +284,25 @@ and windows_hardware_to_xml = function | Other path -> Comment (sprintf "unknown DeviceId: %s" (String.concat "\\" path)) +and get_pci_vendor v = get_hwdata'1 Hwdata.pci_vendor v +and get_pci_device v d = get_hwdata'2 Hwdata.pci_device v d +and get_usb_vendor v = get_hwdata'1 Hwdata.usb_vendor v +and get_usb_device v d = get_hwdata'2 Hwdata.usb_device v d + +and get_hwdata'1 f = function + | Some i64 when i64 >= 0_L && i64 <= 0xffff_L -> + let i32 = Int64.to_int32 i64 in + f i32 + | _ -> None + +and get_hwdata'2 f v d = + match v, d with + | Some v64, Some d64 when v64 >= 0_L && v64 <= 0xffff_L && + d64 >= 0_L && d64 <= 0xffff_L -> + let v32 = Int64.to_int32 v64 and d32 = Int64.to_int32 d64 in + f v32 d32 + | _ -> None + (* Main program. *) let main () = let add, ks = parse_cmdline () in diff --git a/drivers/hwdata.ml b/drivers/hwdata.ml new file mode 100644 index 000000000..4b46eff68 --- /dev/null +++ b/drivers/hwdata.ml @@ -0,0 +1,187 @@ +(* virt-drivers + * Copyright (C) 2009-2023 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + *) + +open Std_utils +open Tools_utils +open Common_gettext.Gettext + +open Printf +open Scanf + +module DBKey = struct + type t = + | Vendor of int32 + | Device of int32 * int32 + let compare = compare +end +module DB = Map.Make (DBKey) + +let is_4_digit_hex id = + String.length id = 4 && + Char.isxdigit id.[0] && + Char.isxdigit id.[1] && + Char.isxdigit id.[2] && + Char.isxdigit id.[3] +let hex_to_int32 id = sscanf id "%lx" identity + +(* Loads one of the [*.ids] files, returning the entries as a + * 3 level map. Returns [None] if the file could not be opened + * or parsed. + *) +let load filename = + try + let lines = read_whole_file filename in + let lines = String.lines_split lines in + + (* This loop drops blank lines and comments, splits the fields of + * the database, and returns [(lineno, indent, key, label) list]. + *) + let rec loop lineno acc = function + | [] -> List.rev acc + (* Blank lines. *) + | "" :: lines -> + loop (lineno+1) acc lines + (* Note that # only starts a comment at the beginning of the line. *) + | comment :: lines when String.is_prefix comment "#" -> + loop (lineno+1) acc lines + (* Otherwise its some data. *) + | line :: lines -> + let len = String.length line in + let indent = + let rec counttabs i = + if i < len && line.[i] = '\t' then 1 + counttabs (i+1) else 0 + in + counttabs 0 in + let line = String.sub line indent (len - indent) in + + let n = String.cspan line " \t" in + let key, label = String.break n line in + let n = String.span label " \t" in + let _, label = String.break n label in + + let acc = + if key = "" && label = "" then acc + else (lineno, indent, key, label) :: acc in + + loop (lineno+1) acc lines + in + let lines = loop 1 [] lines in + + (* Since the format is essentially a space-saving one where + * vendor name + * \t device name + * is short for: + * vendor name + * vendor device name + * pull the fields from previous lines down, resulting in + * a flat list. + *) + let rec loop keys acc = function + | [] -> List.rev acc + | (lineno, indent, key, label) :: lines -> + let prefix = List.take indent keys in + let keys = prefix @ [ key ] in + let acc = (lineno, keys, label) :: acc in + loop keys acc lines + in + let lines = loop [] [] lines in + + (* + List.iter ( + fun (lineno, keys, label) -> + eprintf "[%s] -> %s # line %d\n" + (String.concat ";" keys) label lineno + ) lines; + *) + + (* Now we can finally process the database. + * + * We currently ignore the [C] (class) and other records + * that appear at the end of the file. We might want to + * try parsing these in future. It will require changes to + * the code above because the label isn't parsed right. + *) + let db = + List.fold_left ( + fun db (lineno, keys, label) -> + let loc = filename, lineno in + match keys with + | [vendor] when is_4_digit_hex vendor -> + let vendor = hex_to_int32 vendor in + DB.add (Vendor vendor) (label, loc) db + | [vendor; device] when is_4_digit_hex vendor && + is_4_digit_hex device -> + let vendor = hex_to_int32 vendor in + let device = hex_to_int32 device in + DB.add (Device (vendor, device)) (label, loc) db + | _ -> + db + ) DB.empty lines in + + Some db + with exn -> + warning (f_"hwdata: %s: %s") filename (Printexc.to_string exn); + None + +(* Lazily load the PCI database, if present. *) +let pci_db = + let filename = Hwdata_config.pci_ids in + lazy (match filename with None -> None | Some filename -> load filename) + +(* Look up PCI vendor and device ID. *) +let pci_vendor vendor = + let db = Lazy.force pci_db in + match db with + | None -> None + | Some db -> + match DB.find_opt (Vendor vendor) db with + | None -> None + | Some (label, _) -> Some label + +let pci_device vendor device = + let db = Lazy.force pci_db in + match db with + | None -> None + | Some db -> + match DB.find_opt (Device (vendor, device)) db with + | None -> None + | Some (label, _) -> Some label + +(* Lazily load the USB database, if present. *) +let usb_db = + let filename = Hwdata_config.usb_ids in + lazy (match filename with None -> None | Some filename -> load filename) + +(* Look up USB vendor and device ID. *) +let usb_vendor vendor = + let db = Lazy.force usb_db in + match db with + | None -> None + | Some db -> + match DB.find_opt (Vendor vendor) db with + | None -> None + | Some (label, _) -> Some label + +let usb_device vendor device = + let db = Lazy.force usb_db in + match db with + | None -> None + | Some db -> + match DB.find_opt (Device (vendor, device)) db with + | None -> None + | Some (label, _) -> Some label diff --git a/drivers/hwdata.mli b/drivers/hwdata.mli new file mode 100644 index 000000000..972dfe1f6 --- /dev/null +++ b/drivers/hwdata.mli @@ -0,0 +1,31 @@ +(* virt-drivers + * Copyright (C) 2013-2023 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + *) + +(** Look up PCI and USB vendor and device IDs. *) + +val pci_vendor : int32 -> string option +(** Look up the PCI vendor ID. If found, return the name. *) + +val pci_device : int32 -> int32 -> string option +(** Look up the PCI vendor & device ID. If found, return the name. *) + +val usb_vendor : int32 -> string option +(** Look up the USB vendor ID. If found, return the name. *) + +val usb_device : int32 -> int32 -> string option +(** Look up the USB vendor & device ID. If found, return the name. *) diff --git a/drivers/hwdata_config.ml.in b/drivers/hwdata_config.ml.in new file mode 100644 index 000000000..fa792c086 --- /dev/null +++ b/drivers/hwdata_config.ml.in @@ -0,0 +1,26 @@ +(* virt-drivers + * @configure_input@ + * Copyright (C) 2009-2023 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + *) + +open Std_utils + +let dir = "@HWDATA_PKGDATADIR@" +let dir = if dir = "" then None else Some dir + +let pci_ids = Option.map (fun d -> d // "pci.ids") dir +let usb_ids = Option.map (fun d -> d // "usb.ids") dir diff --git a/drivers/hwdata_config.mli b/drivers/hwdata_config.mli new file mode 100644 index 000000000..877e9e28a --- /dev/null +++ b/drivers/hwdata_config.mli @@ -0,0 +1,35 @@ +(* virt-drivers + * Copyright (C) 2013-2023 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + *) + +val dir : string option +(** [pkgdatadir] variable defined by hwdata.pc + + This is the name of the directory containing [pci.ids] and + related files which contain the PCI IDs. *) + +val pci_ids : string option +(** Path to the [pci.ids] file. + + Note at runtime this is an optional dependency, so it may + not at exist even if not [None]. *) + +val usb_ids : string option +(** Path to the [usb.ids] file. + + Note at runtime this is an optional dependency, so it may + not at exist even if not [None]. *) diff --git a/drivers/test-virt-drivers-windows.sh b/drivers/test-virt-drivers-windows.sh index df3f36c64..4131f6e5e 100755 --- a/drivers/test-virt-drivers-windows.sh +++ b/drivers/test-virt-drivers-windows.sh @@ -22,9 +22,18 @@ $TEST_FUNCTIONS skip_if_skipped skip_unless_phony_guest windows.img -rm -f actual-windows.xml +rm -f actual-windows.xml actual-windows.xml.bak $VG virt-drivers --format=raw -a ../test-data/phony-guests/windows.img > actual-windows.xml + +# We can't predict if hwdata is available, so we don't know if +# vendorname and devicename fields will be present. If present, +# remove them before comparison. +mv actual-windows.xml actual-windows.xml.bak +sed -e "s/ vendorname='\([^']*\)'//g" \ + -e "s/ devicename='\([^']*\)'//g" \ + < actual-windows.xml.bak > actual-windows.xml + diff -ur -I "generated by" expected-windows.xml actual-windows.xml -rm actual-windows.xml +rm actual-windows.xml actual-windows.xml.bak diff --git a/m4/guestfs-libraries.m4 b/m4/guestfs-libraries.m4 index 2d252bf9e..32f93afda 100644 --- a/m4/guestfs-libraries.m4 +++ b/m4/guestfs-libraries.m4 @@ -169,3 +169,6 @@ PKG_CHECK_MODULES([JANSSON], [jansson >= 2.7]) dnl Check for libosinfo (mandatory) PKG_CHECK_MODULES([LIBOSINFO], [libosinfo-1.0]) + +dnl Check for hwdata directory (containing pci.ids) (optional, for virt-drivers) +PKG_CHECK_VAR([HWDATA_PKGDATADIR], [hwdata], [pkgdatadir]) diff --git a/po/POTFILES-ml b/po/POTFILES-ml index 73984796f..7632f374d 100644 --- a/po/POTFILES-ml +++ b/po/POTFILES-ml @@ -79,6 +79,8 @@ dib/output_format_tgz.ml dib/output_format_vhd.ml dib/utils.ml drivers/drivers.ml +drivers/hwdata.ml +drivers/hwdata_config.ml get-kernel/get_kernel.ml resize/resize.ml sparsify/cmdline.ml -- 2.31.1