diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 5e6ca3e1c77a..eb2c88214c27 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -30,6 +30,9 @@
 #include <linux/syscore_ops.h>
 #include <linux/reboot.h>
 #include <linux/security.h>
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/fs.h>
 
 #include <generated/utsrelease.h>
 
@@ -302,8 +305,9 @@ static void fw_finish_direct_load(struct device *device,
 	mutex_unlock(&fw_lock);
 }
 
-static int fw_get_filesystem_firmware(struct device *device,
-				       struct firmware_buf *buf)
+static int
+fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf,
+			   struct file_dma *dma)
 {
 	loff_t size;
 	int i, len;
@@ -328,7 +332,7 @@ static int fw_get_filesystem_firmware(struct device *device,
 
 		buf->size = 0;
 		rc = kernel_read_file_from_path(path, &buf->data, &size,
-						INT_MAX, READING_FIRMWARE);
+						INT_MAX, READING_FIRMWARE, dma);
 		if (rc) {
 			if (rc == -ENOENT)
 				dev_dbg(device, "loading %s failed with error %d\n",
@@ -445,6 +449,7 @@ struct firmware_priv {
 	struct device dev;
 	struct firmware_buf *buf;
 	struct firmware *fw;
+	struct file_dma *dma;
 };
 
 static struct firmware_priv *to_firmware_priv(struct device *dev)
@@ -692,6 +697,60 @@ out:
 
 static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store);
 
+#ifdef CONFIG_HAS_DMA
+static ssize_t firmware_dma_rw(struct file_dma *dma, char *buffer,
+			       loff_t *offset, size_t count, bool read)
+{
+	char *fw_buf;
+	int retval = count;
+
+	if ((dma->offset + *offset + count) > dma->size)
+		return -EINVAL;
+
+	fw_buf = dma_remap(dma->dev, dma->cpu_addr, dma->dma_addr, count,
+			   dma->offset + *offset, dma->attrs);
+	if (!fw_buf)
+		return -ENOMEM;
+
+	if (read)
+		memcpy(buffer, fw_buf, count);
+	else
+		memcpy(fw_buf, buffer, count);
+
+	dma_unremap(dma->dev, fw_buf, count, dma->offset + *offset, dma->attrs);
+	*offset += count;
+
+	return retval;
+}
+#else
+static ssize_t firmware_dma_rw(struct file_dma *dma, char *buffer,
+			       loff_t *offset, size_t count, bool read)
+{
+	return -EINVAL;
+}
+#endif
+
+static void firmware_rw(struct firmware_buf *buf, char *buffer,
+			loff_t offset, size_t count, bool read)
+{
+	while (count) {
+		void *page_data;
+		int page_nr = offset >> PAGE_SHIFT;
+		int page_ofs = offset & (PAGE_SIZE-1);
+		int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
+
+		page_data = kmap(buf->pages[page_nr]);
+
+		memcpy(buffer, page_data + page_ofs, page_cnt);
+
+		kunmap(buf->pages[page_nr]);
+		buffer += page_cnt;
+		offset += page_cnt;
+		count -= page_cnt;
+	}
+
+}
+
 static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
 				  struct bin_attribute *bin_attr,
 				  char *buffer, loff_t offset, size_t count)
@@ -716,21 +775,12 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
 
 	ret_count = count;
 
-	while (count) {
-		void *page_data;
-		int page_nr = offset >> PAGE_SHIFT;
-		int page_ofs = offset & (PAGE_SIZE-1);
-		int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
-
-		page_data = kmap(buf->pages[page_nr]);
-
-		memcpy(buffer, page_data + page_ofs, page_cnt);
+	if (fw_priv->dma)
+		ret_count = firmware_dma_rw(fw_priv->dma, buffer, &offset,
+					    count, true);
+	else
+		firmware_rw(buf, buffer, offset, count, true);
 
-		kunmap(buf->pages[page_nr]);
-		buffer += page_cnt;
-		offset += page_cnt;
-		count -= page_cnt;
-	}
 out:
 	mutex_unlock(&fw_lock);
 	return ret_count;
@@ -792,6 +842,7 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
 {
 	struct device *dev = kobj_to_dev(kobj);
 	struct firmware_priv *fw_priv = to_firmware_priv(dev);
+	struct file_dma *dma = fw_priv->dma;
 	struct firmware_buf *buf;
 	ssize_t retval;
 
@@ -805,26 +856,17 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
 		goto out;
 	}
 
-	retval = fw_realloc_buffer(fw_priv, offset + count);
-	if (retval)
-		goto out;
-
-	retval = count;
-
-	while (count) {
-		void *page_data;
-		int page_nr = offset >> PAGE_SHIFT;
-		int page_ofs = offset & (PAGE_SIZE - 1);
-		int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
-
-		page_data = kmap(buf->pages[page_nr]);
-
-		memcpy(page_data + page_ofs, buffer, page_cnt);
+	if (dma) {
+		retval = firmware_dma_rw(dma, buffer, &offset, count, false);
+		if (retval < 0)
+			goto out;
+	} else {
+		retval = fw_realloc_buffer(fw_priv, offset + count);
+		if (retval)
+			goto out;
 
-		kunmap(buf->pages[page_nr]);
-		buffer += page_cnt;
-		offset += page_cnt;
-		count -= page_cnt;
+		retval = count;
+		firmware_rw(buf, buffer, offset, count, false);
 	}
 
 	buf->size = max_t(size_t, offset, buf->size);
@@ -862,7 +904,8 @@ static const struct attribute_group *fw_dev_attr_groups[] = {
 
 static struct firmware_priv *
 fw_create_instance(struct firmware *firmware, const char *fw_name,
-		   struct device *device, unsigned int opt_flags)
+		   struct device *device, unsigned int opt_flags,
+		   struct file_dma *dma)
 {
 	struct firmware_priv *fw_priv;
 	struct device *f_dev;
@@ -875,6 +918,7 @@ fw_create_instance(struct firmware *firmware, const char *fw_name,
 
 	fw_priv->nowait = !!(opt_flags & FW_OPT_NOWAIT);
 	fw_priv->fw = firmware;
+	fw_priv->dma = dma;
 	f_dev = &fw_priv->dev;
 
 	device_initialize(f_dev);
@@ -895,7 +939,8 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
 	struct firmware_buf *buf = fw_priv->buf;
 
 	/* fall back on userspace loading */
-	buf->is_paged_buf = true;
+	if (!fw_priv->dma)
+		buf->is_paged_buf = true;
 
 	dev_set_uevent_suppress(f_dev, true);
 
@@ -930,7 +975,7 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
 
 	if (is_fw_load_aborted(buf))
 		retval = -EAGAIN;
-	else if (!buf->data)
+	else if (buf->is_paged_buf && !buf->data)
 		retval = -ENOMEM;
 
 	device_del(f_dev);
@@ -941,11 +986,12 @@ err_put_dev:
 
 static int fw_load_from_user_helper(struct firmware *firmware,
 				    const char *name, struct device *device,
-				    unsigned int opt_flags, long timeout)
+				    unsigned int opt_flags, long timeout,
+				    struct file_dma *dma)
 {
 	struct firmware_priv *fw_priv;
 
-	fw_priv = fw_create_instance(firmware, name, device, opt_flags);
+	fw_priv = fw_create_instance(firmware, name, device, opt_flags, dma);
 	if (IS_ERR(fw_priv))
 		return PTR_ERR(fw_priv);
 
@@ -973,7 +1019,7 @@ static void kill_requests_without_uevent(void)
 static inline int
 fw_load_from_user_helper(struct firmware *firmware, const char *name,
 			 struct device *device, unsigned int opt_flags,
-			 long timeout)
+			 long timeout, struct file_dma *dma)
 {
 	return -ENOENT;
 }
@@ -1094,7 +1140,8 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device,
 /* called from request_firmware() and request_firmware_work_func() */
 static int
 _request_firmware(const struct firmware **firmware_p, const char *name,
-		  struct device *device, unsigned int opt_flags)
+		  struct device *device, struct file_dma *dma,
+		  unsigned int opt_flags)
 {
 	struct firmware *fw = NULL;
 	long timeout;
@@ -1131,7 +1178,7 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
 		}
 	}
 
-	ret = fw_get_filesystem_firmware(device, fw->priv);
+	ret = fw_get_filesystem_firmware(device, fw->priv, dma);
 	if (ret) {
 		if (!(opt_flags & FW_OPT_NO_WARN))
 			dev_warn(device,
@@ -1140,7 +1187,8 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
 		if (opt_flags & FW_OPT_USERHELPER) {
 			dev_warn(device, "Falling back to user helper\n");
 			ret = fw_load_from_user_helper(fw, name, device,
-						       opt_flags, timeout);
+						       opt_flags, timeout,
+						       dma);
 		}
 	}
 
@@ -1187,7 +1235,7 @@ request_firmware(const struct firmware **firmware_p, const char *name,
 
 	/* Need to pin this module until return */
 	__module_get(THIS_MODULE);
-	ret = _request_firmware(firmware_p, name, device,
+	ret = _request_firmware(firmware_p, name, device, NULL,
 				FW_OPT_UEVENT | FW_OPT_FALLBACK);
 	module_put(THIS_MODULE);
 	return ret;
@@ -1211,7 +1259,7 @@ int request_firmware_direct(const struct firmware **firmware_p,
 	int ret;
 
 	__module_get(THIS_MODULE);
-	ret = _request_firmware(firmware_p, name, device,
+	ret = _request_firmware(firmware_p, name, device, NULL,
 				FW_OPT_UEVENT | FW_OPT_NO_WARN);
 	module_put(THIS_MODULE);
 	return ret;
@@ -1219,6 +1267,47 @@ int request_firmware_direct(const struct firmware **firmware_p,
 EXPORT_SYMBOL_GPL(request_firmware_direct);
 
 /**
+ * request_firmware_dma - load firmware into a DMA allocated buffer
+ * @firmware_p: pointer to firmware image
+ * @name: name of firmware file
+ * @device: device for which firmware is being loaded and DMA region allocated
+ * @cpu_addr: address returned from dma_alloc_*()
+ * @dma_addr: address returned from dma_alloc_*()
+ * @offset: offset into DMA buffer to copy firmware into
+ * @size: size of DMA buffer
+ * @attrs: attributes used during DMA allocation time
+ *
+ * This function works pretty much like request_firmware(), but it doesn't
+ * load the firmware into @firmware_p's data member. Instead, the firmware
+ * is loaded directly into the buffer pointed to by @cpu_addr and @dma_addr.
+ * This function doesn't cache firmware either.
+ */
+int
+request_firmware_dma(const struct firmware **firmware_p, const char *name,
+		     struct device *device, void *cpu_addr, dma_addr_t dma_addr,
+		     unsigned long offset, size_t size, struct dma_attrs *attrs)
+{
+	int ret;
+	struct file_dma dma = {
+		.dev = device,
+		.cpu_addr = cpu_addr,
+		.dma_addr = dma_addr,
+		.offset = offset,
+		.size = size,
+		.attrs = attrs,
+	};
+
+	/* Need to pin this module until return */
+	__module_get(THIS_MODULE);
+	ret = _request_firmware(firmware_p, name, device, &dma,
+				FW_OPT_UEVENT | FW_OPT_FALLBACK |
+				FW_OPT_NOCACHE);
+	module_put(THIS_MODULE);
+	return ret;
+}
+EXPORT_SYMBOL(request_firmware_dma);
+
+/**
  * release_firmware: - release the resource associated with a firmware image
  * @fw: firmware resource to release
  **/
@@ -1250,7 +1339,7 @@ static void request_firmware_work_func(struct work_struct *work)
 
 	fw_work = container_of(work, struct firmware_work, work);
 
-	_request_firmware(&fw, fw_work->name, fw_work->device,
+	_request_firmware(&fw, fw_work->name, fw_work->device, NULL,
 			  fw_work->opt_flags);
 	fw_work->cont(fw, fw_work->context);
 	put_device(fw_work->device); /* taken in request_firmware_nowait() */
diff --git a/fs/exec.c b/fs/exec.c
index c4010b8207a1..b96ceb5facff 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -57,6 +57,7 @@
 #include <linux/oom.h>
 #include <linux/compat.h>
 #include <linux/vmalloc.h>
+#include <linux/dma-mapping.h>
 
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
@@ -836,12 +837,15 @@ int kernel_read(struct file *file, loff_t offset,
 
 EXPORT_SYMBOL(kernel_read);
 
-int kernel_read_file(struct file *file, void **buf, loff_t *size,
-		     loff_t max_size, enum kernel_read_file_id id)
+static int _kernel_read_file(struct file *file, void **buf, loff_t *size,
+			     loff_t max_size, enum kernel_read_file_id id,
+			     struct file_dma *dma)
 {
-	loff_t i_size, pos;
+	loff_t i_size, r_size = 0, pos, remaining;
 	ssize_t bytes = 0;
 	int ret;
+	unsigned long dma_pos = 0;
+	char *dbuf;
 
 	if (!S_ISREG(file_inode(file)->i_mode) || max_size < 0)
 		return -EINVAL;
@@ -856,33 +860,73 @@ int kernel_read_file(struct file *file, void **buf, loff_t *size,
 	if (i_size <= 0)
 		return -EINVAL;
 
-	*buf = vmalloc(i_size);
-	if (!*buf)
-		return -ENOMEM;
+	if (dma) {
+#ifdef CONFIG_HAS_DMA
+		if (i_size + dma->offset > dma->size)
+			return -EINVAL;
 
-	pos = 0;
-	while (pos < i_size) {
-		bytes = kernel_read(file, pos, (char *)(*buf) + pos,
-				    i_size - pos);
-		if (bytes < 0) {
-			ret = bytes;
-			goto out;
+		dma_pos = dma->offset;
+		pos = 0;
+		remaining = i_size;
+
+		while (remaining > 0) {
+			r_size = min_t(int, remaining, PAGE_SIZE);
+
+			dbuf = dma_remap(dma->dev, dma->cpu_addr, dma->dma_addr,
+					r_size, dma_pos, dma->attrs);
+			if (!dbuf)
+				return -ENOMEM;
+
+			ret = kernel_read(file, pos, dbuf, r_size);
+			if (ret != r_size) {
+				if (ret > 0)
+					ret = -EIO;
+				goto fail_dma;
+			}
+
+			dma_unremap(dma->dev, dbuf, r_size, dma_pos, dma->attrs);
+			dma_pos += r_size;
+			pos += r_size;
+			remaining -= r_size;
 		}
+#else
+		return -EINVAL;
+#endif
+	} else {
 
-		if (bytes == 0)
-			break;
-		pos += bytes;
-	}
+		*buf = vmalloc(i_size);
+		if (!*buf)
+			return -ENOMEM;
 
-	if (pos != i_size) {
-		ret = -EIO;
-		goto out;
+		pos = 0;
+		while (pos < i_size) {
+			bytes = kernel_read(file, pos, (char *)(*buf) + pos,
+					    i_size - pos);
+			if (bytes < 0) {
+				ret = bytes;
+				goto out;
+			}
+
+			if (bytes == 0)
+				break;
+			pos += bytes;
+		}
+
+		if (pos != i_size) {
+			ret = -EIO;
+			goto out;
+		}
 	}
 
 	ret = security_kernel_post_read_file(file, *buf, i_size, id);
 	if (!ret)
 		*size = pos;
 
+#ifdef CONFIG_HAS_DMA
+fail_dma:
+	dma_unremap(dma->dev, buf, r_size, dma_pos, dma->attrs);
+	return ret;
+#endif
 out:
 	if (ret < 0) {
 		vfree(*buf);
@@ -890,10 +934,17 @@ out:
 	}
 	return ret;
 }
+
+int kernel_read_file(struct file *file, void **buf, loff_t *size,
+		     loff_t max_size, enum kernel_read_file_id id)
+{
+	return _kernel_read_file(file, buf, size, max_size, id, NULL);
+}
 EXPORT_SYMBOL_GPL(kernel_read_file);
 
 int kernel_read_file_from_path(char *path, void **buf, loff_t *size,
-			       loff_t max_size, enum kernel_read_file_id id)
+			       loff_t max_size, enum kernel_read_file_id id,
+			       struct file_dma *dma)
 {
 	struct file *file;
 	int ret;
@@ -905,7 +956,7 @@ int kernel_read_file_from_path(char *path, void **buf, loff_t *size,
 	if (IS_ERR(file))
 		return PTR_ERR(file);
 
-	ret = kernel_read_file(file, buf, size, max_size, id);
+	ret = _kernel_read_file(file, buf, size, max_size, id, dma);
 	fput(file);
 	return ret;
 }
diff --git a/include/linux/firmware.h b/include/linux/firmware.h
index 5c41c5e75b5c..06bc8d5965ca 100644
--- a/include/linux/firmware.h
+++ b/include/linux/firmware.h
@@ -19,6 +19,7 @@ struct firmware {
 
 struct module;
 struct device;
+struct dma_attrs;
 
 struct builtin_fw {
 	char *name;
@@ -47,6 +48,10 @@ int request_firmware_nowait(
 	void (*cont)(const struct firmware *fw, void *context));
 int request_firmware_direct(const struct firmware **fw, const char *name,
 			    struct device *device);
+int request_firmware_dma(const struct firmware **firmware_p, const char *name,
+		     struct device *device, void *cpu_addr, dma_addr_t dma_addr,
+		     unsigned long offset, size_t size,
+		     struct dma_attrs *attrs);
 
 void release_firmware(const struct firmware *fw);
 #else
@@ -75,5 +80,13 @@ static inline int request_firmware_direct(const struct firmware **fw,
 	return -EINVAL;
 }
 
+static inline int request_firmware_dma(const struct firmware **firmware_p,
+	const char *name, struct device *device, void *cpu_addr,
+	dma_addr_t dma_addr, unsigned long offset, size_t size,
+	struct dma_attrs *attrs)
+{
+	return -EINVAL;
+}
+
 #endif
 #endif
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 14a97194b34b..d598944e0700 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -55,6 +55,8 @@ struct workqueue_struct;
 struct iov_iter;
 struct fscrypt_info;
 struct fscrypt_operations;
+struct device;
+struct dma_attrs;
 
 extern void __init inode_init(void);
 extern void __init inode_init_early(void);
@@ -2589,11 +2591,21 @@ enum kernel_read_file_id {
 	READING_MAX_ID
 };
 
+struct file_dma {
+	struct device *dev;
+	void *cpu_addr;
+	dma_addr_t dma_addr;
+	unsigned long offset;
+	size_t size;
+	struct dma_attrs *attrs;
+};
+
 extern int kernel_read(struct file *, loff_t, char *, unsigned long);
 extern int kernel_read_file(struct file *, void **, loff_t *, loff_t,
 			    enum kernel_read_file_id);
 extern int kernel_read_file_from_path(char *, void **, loff_t *, loff_t,
-				      enum kernel_read_file_id);
+				      enum kernel_read_file_id,
+				      struct file_dma *dma);
 extern int kernel_read_file_from_fd(int, void **, loff_t *, loff_t,
 				    enum kernel_read_file_id);
 extern ssize_t kernel_write(struct file *, const char *, size_t, loff_t);
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 60d011aaec38..b922d6ca2fb0 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -272,7 +272,8 @@ static ssize_t ima_read_policy(char *path)
 	datap = path;
 	strsep(&datap, "\n");
 
-	rc = kernel_read_file_from_path(path, &data, &size, 0, READING_POLICY);
+	rc = kernel_read_file_from_path(path, &data, &size, 0, READING_POLICY,
+					NULL);
 	if (rc < 0) {
 		pr_err("Unable to open file: %s (%d)", path, rc);
 		return rc;
