guestfs-tools/SOURCES/0004-drivers-Look-up-vendor...

481 lines
16 KiB
Diff

From ec06f62df5340cd0a9466a532aa9806fb0e2e560 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
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