#include <linux/init.h>
 #include <linux/crash_dump.h>
 #include <linux/list.h>
+#include <linux/mutex.h>
 #include <linux/vmalloc.h>
 #include <linux/pagemap.h>
 #include <linux/uaccess.h>
 
 static struct proc_dir_entry *proc_vmcore;
 
+#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
+/* Device Dump list and mutex to synchronize access to list */
+static LIST_HEAD(vmcoredd_list);
+static DEFINE_MUTEX(vmcoredd_mutex);
+#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
+
 /*
  * Returns > 0 for RAM pages, 0 for non-RAM pages, < 0 on error
  * The called function has to take care of module refcounting.
 };
 
 /**
- * alloc_elfnotes_buf - allocate buffer for ELF note segment in
- *                      vmalloc memory
- *
- * @notes_sz: size of buffer
+ * vmcore_alloc_buf - allocate buffer in vmalloc memory
+ * @sizez: size of buffer
  *
  * If CONFIG_MMU is defined, use vmalloc_user() to allow users to mmap
  * the buffer to user-space by means of remap_vmalloc_range().
  * If CONFIG_MMU is not defined, use vzalloc() since mmap_vmcore() is
  * disabled and there's no need to allow users to mmap the buffer.
  */
-static inline char *alloc_elfnotes_buf(size_t notes_sz)
+static inline char *vmcore_alloc_buf(size_t size)
 {
 #ifdef CONFIG_MMU
-       return vmalloc_user(notes_sz);
+       return vmalloc_user(size);
 #else
-       return vzalloc(notes_sz);
+       return vzalloc(size);
 #endif
 }
 
                return rc;
 
        *notes_sz = roundup(phdr_sz, PAGE_SIZE);
-       *notes_buf = alloc_elfnotes_buf(*notes_sz);
+       *notes_buf = vmcore_alloc_buf(*notes_sz);
        if (!*notes_buf)
                return -ENOMEM;
 
                return rc;
 
        *notes_sz = roundup(phdr_sz, PAGE_SIZE);
-       *notes_buf = alloc_elfnotes_buf(*notes_sz);
+       *notes_buf = vmcore_alloc_buf(*notes_sz);
        if (!*notes_buf)
                return -ENOMEM;
 
        return 0;
 }
 
+#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
+/**
+ * vmcoredd_write_header - Write vmcore device dump header at the
+ * beginning of the dump's buffer.
+ * @buf: Output buffer where the note is written
+ * @data: Dump info
+ * @size: Size of the dump
+ *
+ * Fills beginning of the dump's buffer with vmcore device dump header.
+ */
+static void vmcoredd_write_header(void *buf, struct vmcoredd_data *data,
+                                 u32 size)
+{
+       struct vmcoredd_header *vdd_hdr = (struct vmcoredd_header *)buf;
+
+       vdd_hdr->n_namesz = sizeof(vdd_hdr->name);
+       vdd_hdr->n_descsz = size + sizeof(vdd_hdr->dump_name);
+       vdd_hdr->n_type = NT_VMCOREDD;
+
+       strncpy((char *)vdd_hdr->name, VMCOREDD_NOTE_NAME,
+               sizeof(vdd_hdr->name));
+       memcpy(vdd_hdr->dump_name, data->dump_name, sizeof(vdd_hdr->dump_name));
+}
+
+/**
+ * vmcore_add_device_dump - Add a buffer containing device dump to vmcore
+ * @data: dump info.
+ *
+ * Allocate a buffer and invoke the calling driver's dump collect routine.
+ * Write Elf note at the beginning of the buffer to indicate vmcore device
+ * dump and add the dump to global list.
+ */
+int vmcore_add_device_dump(struct vmcoredd_data *data)
+{
+       struct vmcoredd_node *dump;
+       void *buf = NULL;
+       size_t data_size;
+       int ret;
+
+       if (!data || !strlen(data->dump_name) ||
+           !data->vmcoredd_callback || !data->size)
+               return -EINVAL;
+
+       dump = vzalloc(sizeof(*dump));
+       if (!dump) {
+               ret = -ENOMEM;
+               goto out_err;
+       }
+
+       /* Keep size of the buffer page aligned so that it can be mmaped */
+       data_size = roundup(sizeof(struct vmcoredd_header) + data->size,
+                           PAGE_SIZE);
+
+       /* Allocate buffer for driver's to write their dumps */
+       buf = vmcore_alloc_buf(data_size);
+       if (!buf) {
+               ret = -ENOMEM;
+               goto out_err;
+       }
+
+       vmcoredd_write_header(buf, data, data_size -
+                             sizeof(struct vmcoredd_header));
+
+       /* Invoke the driver's dump collection routing */
+       ret = data->vmcoredd_callback(data, buf +
+                                     sizeof(struct vmcoredd_header));
+       if (ret)
+               goto out_err;
+
+       dump->buf = buf;
+       dump->size = data_size;
+
+       /* Add the dump to driver sysfs list */
+       mutex_lock(&vmcoredd_mutex);
+       list_add_tail(&dump->list, &vmcoredd_list);
+       mutex_unlock(&vmcoredd_mutex);
+
+       return 0;
+
+out_err:
+       if (buf)
+               vfree(buf);
+
+       if (dump)
+               vfree(dump);
+
+       return ret;
+}
+EXPORT_SYMBOL(vmcore_add_device_dump);
+#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
+
+/* Free all dumps in vmcore device dump list */
+static void vmcore_free_device_dumps(void)
+{
+#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
+       mutex_lock(&vmcoredd_mutex);
+       while (!list_empty(&vmcoredd_list)) {
+               struct vmcoredd_node *dump;
+
+               dump = list_first_entry(&vmcoredd_list, struct vmcoredd_node,
+                                       list);
+               list_del(&dump->list);
+               vfree(dump->buf);
+               vfree(dump);
+       }
+       mutex_unlock(&vmcoredd_mutex);
+#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
+}
+
 /* Init function for vmcore module. */
 static int __init vmcore_init(void)
 {
                kfree(m);
        }
        free_elfcorebuf();
+
+       /* clear vmcore device dump list */
+       vmcore_free_device_dumps();
 }