Firmware loading overhaul
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 42ec62c..7684199 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -6,7 +6,7 @@
  * Please see Documentation/firmware_class/ for more information.
  *
  */
-
+#define DEBUG
 #include <linux/capability.h>
 #include <linux/device.h>
 #include <linux/module.h>
@@ -160,7 +160,6 @@
 	struct list_head pending_list;
 #endif
 	const char *fw_id;
-	const char *fw_sig;
 };
 
 struct fw_cache_entry {
@@ -186,23 +185,24 @@
 
 static struct firmware_cache fw_cache;
 
-static struct firmware_buf *__allocate_fw_buf(const char *fw_name,
-					      struct firmware_cache *fwc)
+/*
+ * Allocate and initialise a firmware buffer record.
+ */
+static struct firmware_buf *fw_allocate_buf(const char *fw_name,
+					    struct firmware_cache *fwc)
 {
 	struct firmware_buf *buf;
 
-	buf = kzalloc(sizeof(*buf), GFP_ATOMIC);
+	pr_devel("%s(%s)\n", __func__, fw_name);
+
+	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
 	if (!buf)
 		goto err;
 
-	buf->fw_id = kstrdup_const(fw_name, GFP_ATOMIC);
+	buf->fw_id = kstrdup_const(fw_name, GFP_KERNEL);
 	if (!buf->fw_id)
 		goto err_buf;
 
-	buf->fw_sig = kasprintf(GFP_ATOMIC, "%s.p7s", buf->fw_id);
-	if (buf->fw_sig)
-		goto err_id;
-
 	kref_init(&buf->ref);
 	buf->fwc = fwc;
 	init_completion(&buf->completion);
@@ -213,49 +213,16 @@
 	pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf);
 	return buf;
 
-err_id:
-	kfree_const(buf->fw_id);
 err_buf:
 	kfree(buf);
 err:
-	return NULL;
+	return ERR_PTR(-ENOMEM);
 }
 
-static struct firmware_buf *__fw_lookup_buf(const char *fw_name)
-{
-	struct firmware_buf *tmp;
-	struct firmware_cache *fwc = &fw_cache;
-
-	list_for_each_entry(tmp, &fwc->head, list)
-		if (!strcmp(tmp->fw_id, fw_name))
-			return tmp;
-	return NULL;
-}
-
-static int fw_lookup_or_allocate_buf(const char *fw_name,
-				      struct firmware_cache *fwc,
-				      struct firmware_buf **buf)
-{
-	struct firmware_buf *tmp;
-
-	spin_lock(&fwc->lock);
-	tmp = __fw_lookup_buf(fw_name);
-	if (tmp) {
-		kref_get(&tmp->ref);
-		spin_unlock(&fwc->lock);
-		*buf = tmp;
-		return 1;
-	}
-
-	tmp = __allocate_fw_buf(fw_name, fwc);
-	if (tmp)
-		list_add(&tmp->list, &fwc->head);
-	spin_unlock(&fwc->lock);
-
-	*buf = tmp;
-	return tmp ? 0 : -ENOMEM;
-}
-
+/*
+ * Free up a firmware buffer record and all the bits dangling off of
+ * it.
+ */
 static void __fw_free_buf(struct kref *ref)
 	__releases(&fwc->lock)
 {
@@ -280,7 +247,6 @@
 #endif
 		vfree(buf->data);
 	kfree_const(buf->fw_id);
-	kfree(buf->fw_sig);
 	kfree(buf);
 }
 
@@ -292,6 +258,49 @@
 		spin_unlock(&fwc->lock);
 }
 
+static struct firmware_buf *__fw_lookup_buf(const char *fw_name)
+{
+	struct firmware_buf *tmp;
+	struct firmware_cache *fwc = &fw_cache;
+
+	list_for_each_entry(tmp, &fwc->head, list)
+		if (!strcmp(tmp->fw_id, fw_name))
+			return tmp;
+	return NULL;
+}
+
+static int fw_lookup_or_allocate_buf(const char *fw_name,
+				     struct firmware_cache *fwc,
+				     struct firmware_buf **_buf)
+{
+	struct firmware_buf *buf, *proposed;
+
+	pr_devel("%s(%s)\n", __func__, fw_name);
+
+	/* Speculatively allocate a firmware buffer, on the likelyhood
+	 * that we'll need it.
+	 */
+	proposed = fw_allocate_buf(fw_name, fwc);
+	if (IS_ERR(proposed))
+		return PTR_ERR(proposed);
+
+	spin_lock(&fwc->lock);
+	buf = __fw_lookup_buf(fw_name);
+	if (buf) {
+		kref_get(&buf->ref);
+		*_buf = buf;
+		__fw_free_buf(&proposed->ref);
+		pr_devel("%s() = 1\n", __func__);
+		return 1;
+	}
+
+	list_add(&proposed->list, &fwc->head);
+	spin_unlock(&fwc->lock);
+
+	*_buf = proposed;
+	return proposed ? 0 : -ENOMEM;
+}
+
 /* direct firmware loading support */
 static char fw_path_para[256];
 static const char * const fw_path[] = {
@@ -310,206 +319,133 @@
 module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644);
 MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path");
 
-static int __read_file_contents(struct file *file,
-				void **dest_buf, size_t *dest_size)
+/*
+ * Read the contents of a file.  This is used for both the firmware blob and
+ * the signature.
+ */
+static int fw_read_file(const char *path, void **_buf, size_t *_size)
 {
-	int size;
+	struct file *file;
+	size_t size;
 	char *buf;
 	int rc;
 
+	pr_devel("%s(%s)\n", __func__, path);
+
+	file = filp_open(path, O_RDONLY, 0);
+	if (IS_ERR(file)) {
+		pr_devel("%s() = %ld [open]\n", __func__, PTR_ERR(file));
+		return PTR_ERR(file);
+	}
+
+	rc = -EINVAL;
 	if (!S_ISREG(file_inode(file)->i_mode))
-		return -EINVAL;
+		goto err_file;
 	size = i_size_read(file_inode(file));
 	if (size <= 0)
-		return -EINVAL;
+		goto err_file;
+	rc = -ENOMEM;
 	buf = vmalloc(size);
 	if (!buf)
-		return -ENOMEM;
+		goto err_file;
+
 	rc = kernel_read(file, 0, buf, size);
+	if (rc < 0)
+		goto err_buf;
 	if (rc != size) {
-		if (rc > 0)
-			rc = -EIO;
-		goto fail;
+		rc = -EIO;
+		goto err_buf;
 	}
+
 	rc = security_kernel_fw_from_file(file, buf, size);
 	if (rc)
-		goto fail;
+		goto err_buf;
 
-	*dest_buf = buf;
-	*dest_size = size;
+	*_buf = buf;
+	*_size = size;
+	pr_devel("%s() = 0 [size %zu]\n", __func__, size);
 	return 0;
 
-fail:
+err_buf:
 	vfree(buf);
+err_file:
+	fput(file);
+	pr_devel("%s() = %d\n", __func__, rc);
 	return rc;
 }
 
-#ifdef CONFIG_FIRMWARE_SIG_FORCE
-static struct file *get_filesystem_file_sig(const char *sig_name)
-{
-	return filp_open(sig_name, O_RDONLY, 0);
-}
-
-static int get_filesystem_file_sig_ok(struct file *file_sig)
-{
-	if (IS_ERR(file_sig))
-		return -EINVAL;
-	return 0;
-}
-
-static int read_file_signature_contents(struct file *file_sig,
-					struct firmware_buf *fw_buf)
-{
-	return __read_file_contents(file_sig,
-				    &fw_buf->data_sig,
-				    &fw_buf->size_sig);
-}
-
-#elif CONFIG_FIRMWARE_SIG
-static struct file *get_filesystem_file_sig(const char *sig_name)
-{
-	struct file *file;
-
-	file = filp_open(sig_name, O_RDONLY, 0);
-	if (IS_ERR(file))
-		pr_info("signature %s not present, but this is OK\n", sig_name);
-
-	return file;
-}
-
-static int get_filesystem_file_sig_ok(struct file *file_sig)
-{
-	return 0;
-}
-
-static int read_file_signature_contents(struct file *file_sig,
-					struct firmware_buf *fw_buf)
-{
-	int rc;
-
-	rc = __read_file_contents(file_sig,
-				  &fw_buf->data_sig,
-				  &fw_buf->size_sig);
-	if (rc)
-		pr_info("could not read signature %s, but this is OK\n",
-			fw_buf->fw_sig);
-
-	return 0;
-}
-#else
-static struct file *get_filesystem_file_sig(const char *sig_name)
-{
-	return NULL;
-}
-
-static int get_filesystem_file_sig_ok(struct file *file_sig)
-{
-	return 0;
-}
-
-static int read_file_signature_contents(struct file *file_sig,
-					struct firmware_buf *fw_buf)
-{
-	return 0;
-}
-#endif
-
-static int fw_read_file_contents(struct file *file,
-				 struct file *file_sig,
-				 struct firmware_buf *fw_buf)
-{
-	int rc;
-
-	rc = __read_file_contents(file,
-				  &fw_buf->data,
-				  &fw_buf->size);
-	if (rc)
-		return rc;
-
-	rc = read_file_signature_contents(file_sig, fw_buf);
-	if (rc)
-		return rc;
-
-	return 0;
-}
-
+/*
+ * Read a firmware blob and its signature.
+ */
 static int fw_get_filesystem_firmware(struct device *device,
-				       struct firmware_buf *buf)
+				      struct firmware_buf *buf)
 {
+	static const char pkcs7_suffix[] = ".p7s";
 	int i, len;
-	int rc = -ENOMEM;
-	char *path, *path_sig = NULL;
+	int rc = -ENOENT;
+	char *path = NULL;
+
+	pr_devel("%s(%s)\n", __func__, buf->fw_id);
 
 	path = __getname();
 	if (!path)
 		return -ENOMEM;
 
-	path_sig = __getname();
-	if (!path_sig)
-		goto out;
-
+	/* Try each possible firmware blob in turn till one doesn't produce
+	 * ENOENT.
+	 */
 	for (i = 0; i < ARRAY_SIZE(fw_path); i++) {
-		struct file *file;
-		struct file *file_sig;
-
 		/* skip the unset customized path */
 		if (!fw_path[i][0])
 			continue;
 
-		len = snprintf(path, PATH_MAX, "%s/%s",
-			       fw_path[i], buf->fw_id);
-		if (len >= PATH_MAX) {
+		len = snprintf(path, PATH_MAX - sizeof(pkcs7_suffix) - 1,
+			       "%s/%s", fw_path[i], buf->fw_id);
+		if (len >= PATH_MAX - sizeof(pkcs7_suffix) - 1) {
 			rc = -ENAMETOOLONG;
-			break;
-		}
-
-		len = snprintf(path_sig, PATH_MAX, "%s/%s",
-			       fw_path[i], buf->fw_sig);
-		if (len >= PATH_MAX) {
-			rc = -ENAMETOOLONG;
-			break;
-		}
-
-		file = filp_open(path, O_RDONLY, 0);
-		if (IS_ERR(file)) {
-			rc = -ENOENT;
 			continue;
 		}
 
-		file_sig = get_filesystem_file_sig(path_sig);
-		rc = get_filesystem_file_sig_ok(file_sig);
-		if (rc) {
-			fput(file);
-			if (!IS_ERR(file_sig))
-				fput(file_sig);
+		rc = fw_read_file(path, &buf->data, &buf->size);
+		if (rc == 0)
+			goto read_firmware_blob;
+
+		if (rc == -ENOENT)
 			continue;
-		}
 
-		rc = fw_read_file_contents(file, file_sig, buf);
-
-		fput(file);
-		if (!IS_ERR(file_sig))
-			fput(file_sig);
-
-		if (rc)
-			dev_warn(device, "firmware, attempted to load %s, but failed with error %d\n",
-				path, rc);
-		else
-			break;
+		dev_warn(device, "firmware, attempted to load %s, but failed with error %d\n",
+			 path, rc);
+		goto out;
 	}
+	goto out;
+
+read_firmware_blob:
+	/* Read the signature associated with the blob */
+	if (IS_ENABLED(CONFIG_FIRMWARE_SIG)) {
+		memcpy(path + len, pkcs7_suffix, sizeof(pkcs7_suffix));
+
+		rc = fw_read_file(path, &buf->data_sig, &buf->size_sig);
+		if (rc == -ENOENT && !fw_sig_enforce) {
+			dev_info(device, "Could not find signature %s, but this is OK\n",
+				 path);
+			rc = 0;
+		}
+		if (rc < 0) {
+			dev_warn(device, "Firmware signature unreadable (%d): '%s'\n",
+				 rc, path);
+			rc = 0; // -EKEYREJECTED;
+		}
+	}
+
+	dev_dbg(device, "firmware: direct-loading firmware %s\n", buf->fw_id);
+	mutex_lock(&fw_lock);
+	set_bit(FW_STATUS_DONE, &buf->status);
+	complete_all(&buf->completion);
+	mutex_unlock(&fw_lock);
+
 out:
 	__putname(path);
-	__putname(path_sig);
-
-	if (!rc) {
-		dev_dbg(device, "firmware: direct-loading firmware %s\n",
-			buf->fw_id);
-		mutex_lock(&fw_lock);
-		set_bit(FW_STATUS_DONE, &buf->status);
-		complete_all(&buf->completion);
-		mutex_unlock(&fw_lock);
-	}
-
+	pr_devel("%s() = %d\n", __func__, rc);
 	return rc;
 }
 
@@ -540,19 +476,23 @@
 }
 
 #ifdef CONFIG_FIRMWARE_SIG
-static int firmware_sig_check(struct firmware *fw, const char *name)
+static int firmware_sig_check(struct firmware *fw, const char *fw_name)
 {
 	int err = -ENOKEY;
 	struct firmware_buf *buf = fw->priv;
 	const void *data = buf->data;
 	const void *data_sig = buf->data_sig;
 
-	err = system_verify_data(data, buf->size, data_sig, buf->size_sig,
-				 KEY_VERIFYING_FIRMWARE_SIGNATURE, name);
-	if (!err) {
-		buf->sig_ok = true;
-		fw_set_page_data(buf, fw);
-		return 0;
+	pr_devel("%s(%s)\n", __func__, fw_name);
+	if (data_sig) {
+		err = system_verify_data(data, buf->size, data_sig, buf->size_sig,
+					 KEY_VERIFYING_FIRMWARE_SIGNATURE, fw_name);
+		if (!err) {
+			buf->sig_ok = true;
+			fw_set_page_data(buf, fw);
+			pr_devel("%s() = 0\n", __func__);
+			return 0;
+		}
 	}
 
 	/* Not having a signature is only an error if we're strict. */
@@ -561,6 +501,7 @@
 
 	fw_set_page_data(buf, fw);
 
+	pr_devel("%s() = %d [fail]\n", __func__, err);
 	return err;
 }
 #else /* !CONFIG_FIRMWARE_SIG */
@@ -1367,7 +1308,6 @@
 	}
 
  out_bad_sig:
-
 	if (ret < 0) {
 		release_firmware(fw);
 		fw = NULL;