pci: add a helper to lookup a pci_device by string

This is for #device code that often wants to find a pci_device that
userspace specified by string.  The string is of the form 00:00.0.  You
can skip leading zeros.

This one throws on error, since the intent is that user-interface code
will use it.  The older match_tbdf() is used in a few internal places,
with a custom format for the int argument (MKBUS()).

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
diff --git a/kern/arch/x86/pci.c b/kern/arch/x86/pci.c
index 58f16e8..a3057d3 100644
--- a/kern/arch/x86/pci.c
+++ b/kern/arch/x86/pci.c
@@ -632,14 +632,9 @@
 	spin_unlock_irqsave(&pcidev->lock);
 }
 
-struct pci_device *pci_match_tbdf(int tbdf)
+static struct pci_device *pci_match_bus_dev_func(int bus, int dev, int func)
 {
 	struct pci_device *search;
-	int bus, dev, func;
-
-	bus = BUSBNO(tbdf);
-	dev = BUSDNO(tbdf);
-	func = BUSFNO(tbdf);
 
 	STAILQ_FOREACH(search, &pci_devices, all_dev) {
 		if ((search->bus == bus) &&
@@ -650,6 +645,30 @@
 	return NULL;
 }
 
+struct pci_device *pci_match_tbdf(int tbdf)
+{
+	return pci_match_bus_dev_func(BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
+}
+
+/* Throws on error */
+struct pci_device *pci_match_string(const char *bdf)
+{
+	struct pci_device *pdev;
+	int bus, dev, func, ret;
+
+	ret = sscanf(bdf, "%x:%x.%x", &bus, &dev, &func);
+	if (ret != 3)
+		error(EINVAL, "Bad BDF format %s", bdf);
+	if ((bus != (bus & 0xff)) ||
+	    (dev != (dev & 0x1f)) ||
+	    (func != (func & 0x07)))
+		error(EINVAL, "BDF %s out of range", bdf);
+	pdev = pci_match_tbdf(MKBUS(BusPCI, bus, dev, func));
+	if (!pdev)
+		error(ENOENT, "No BDF %s", bdf);
+	return pdev;
+}
+
 /* Helper to get the membar value for BAR index bir */
 uintptr_t pci_get_membar(struct pci_device *pcidev, int bir)
 {
diff --git a/kern/arch/x86/pci.h b/kern/arch/x86/pci.h
index 86b9793..9040623 100644
--- a/kern/arch/x86/pci.h
+++ b/kern/arch/x86/pci.h
@@ -300,6 +300,7 @@
 void pci_set_bus_master(struct pci_device *pcidev);
 void pci_clr_bus_master(struct pci_device *pcidev);
 struct pci_device *pci_match_tbdf(int tbdf);
+struct pci_device *pci_match_string(const char *bdf);
 uintptr_t pci_get_membar(struct pci_device *pcidev, int bir);
 uintptr_t pci_get_iobar(struct pci_device *pcidev, int bir);
 bool pci_bar_is_mem32(struct pci_device *pdev, int bar);