diff --git a/docs/misc/xen-command-line.markdown b/docs/misc/xen-command-line.markdown
index 0138978..a11fdf9 100644
--- a/docs/misc/xen-command-line.markdown
+++ b/docs/misc/xen-command-line.markdown
@@ -1377,6 +1377,19 @@ Specify the host reboot method.
 'efi' instructs Xen to reboot using the EFI reboot call (in EFI mode by
  default it will use that method first).
 
+### rmrr
+> '= start<-end>=[s1]bdf1[,[s1]bdf2[,...]];start<-end>=[s2]bdf1[,[s2]bdf2[,...]]
+
+Define RMRR units that are missing from ACPI table along with device they
+belong to and use them for 1:1 mapping. End addresses can be omitted and one
+page will be mapped. The ranges are inclusive when start and end are specified.
+If segment of the first device is not specified, segment zero will be used.
+If other segments are not specified, first device segment will be used.
+If a segment is specified for other than the first device and it does not match
+the one specified for the first one, an error will be reported.
+Note: grub2 requires to escape or use quotations if special characters are used,
+namely ';', refer to the grub2 documentation if multiple ranges are specified.
+
 ### ro-hpet
 > `= <boolean>`
 
diff --git a/xen/drivers/passthrough/vtd/dmar.c b/xen/drivers/passthrough/vtd/dmar.c
index 84bf63d..23ba7a6 100644
--- a/xen/drivers/passthrough/vtd/dmar.c
+++ b/xen/drivers/passthrough/vtd/dmar.c
@@ -859,6 +859,132 @@ out:
     return ret;
 }
 
+#define MAX_EXTRA_RMRR_PAGES 16
+#define MAX_EXTRA_RMRR 10
+
+/* RMRR units derived from command line rmrr option. */
+#define MAX_EXTRA_RMRR_DEV 20
+struct user_rmrr {
+    struct list_head list;
+    unsigned long base_pfn, end_pfn;
+    unsigned int dev_count;
+    u32 sbdf[MAX_EXTRA_RMRR_DEV];
+};
+
+static __initdata unsigned int nr_rmrr;
+static struct __initdata user_rmrr user_rmrrs[MAX_EXTRA_RMRR];
+
+/* Macro for RMRR inclusive range formatting. */
+#define ERMRRU_FMT "[%lx-%lx]"
+#define ERMRRU_ARG(eru) eru.base_pfn, eru.end_pfn
+
+static int __init add_user_rmrr(void)
+{
+    struct acpi_rmrr_unit *rmrr, *rmrru;
+    unsigned int idx, seg, i;
+    unsigned long base, end;
+    bool overlap;
+
+    for ( i = 0; i < nr_rmrr; i++ )
+    {
+        base = user_rmrrs[i].base_pfn;
+        end = user_rmrrs[i].end_pfn;
+
+        if ( base > end )
+        {
+            printk(XENLOG_ERR VTDPREFIX
+                   "Invalid RMRR Range "ERMRRU_FMT"\n",
+                   ERMRRU_ARG(user_rmrrs[i]));
+            continue;
+        }
+
+        if ( (end - base) >= MAX_EXTRA_RMRR_PAGES )
+        {
+            printk(XENLOG_ERR VTDPREFIX
+                   "RMRR range "ERMRRU_FMT" exceeds "\
+                   __stringify(MAX_EXTRA_RMRR_PAGES)" pages\n",
+                   ERMRRU_ARG(user_rmrrs[i]));
+            continue;
+        }
+
+        overlap = false;
+        list_for_each_entry(rmrru, &acpi_rmrr_units, list)
+        {
+            if ( pfn_to_paddr(base) < rmrru->end_address &&
+                 rmrru->base_address < pfn_to_paddr(end + 1) )
+            {
+                printk(XENLOG_ERR VTDPREFIX
+                       "Overlapping RMRRs: "ERMRRU_FMT" and [%lx-%lx]\n",
+                       ERMRRU_ARG(user_rmrrs[i]),
+                       paddr_to_pfn(rmrru->base_address),
+                       paddr_to_pfn(rmrru->end_address));
+                overlap = true;
+                break;
+            }
+        }
+        /* Don't add overlapping RMRR. */
+        if ( overlap )
+            continue;
+
+        do
+        {
+            if ( !mfn_valid(base) )
+            {
+                printk(XENLOG_ERR VTDPREFIX
+                       "Invalid pfn in RMRR range "ERMRRU_FMT"\n",
+                       ERMRRU_ARG(user_rmrrs[i]));
+                break;
+            }
+        } while ( base++ < end );
+
+        /* Invalid pfn in range as the loop ended before end_pfn was reached. */
+        if ( base <= end )
+            continue;
+
+        rmrr = xzalloc(struct acpi_rmrr_unit);
+        if ( !rmrr )
+            return -ENOMEM;
+
+        rmrr->scope.devices = xmalloc_array(u16, user_rmrrs[i].dev_count);
+        if ( !rmrr->scope.devices )
+        {
+            xfree(rmrr);
+            return -ENOMEM;
+        }
+
+        seg = 0;
+        for ( idx = 0; idx < user_rmrrs[i].dev_count; idx++ )
+        {
+            rmrr->scope.devices[idx] = user_rmrrs[i].sbdf[idx];
+            seg |= PCI_SEG(user_rmrrs[i].sbdf[idx]);
+        }
+        if ( seg != PCI_SEG(user_rmrrs[i].sbdf[0]) )
+        {
+            printk(XENLOG_ERR VTDPREFIX
+                   "Segments are not equal for RMRR range "ERMRRU_FMT"\n",
+                   ERMRRU_ARG(user_rmrrs[i]));
+            scope_devices_free(&rmrr->scope);
+            xfree(rmrr);
+            continue;
+        }
+
+        rmrr->segment = seg;
+        rmrr->base_address = pfn_to_paddr(user_rmrrs[i].base_pfn);
+        rmrr->end_address = pfn_to_paddr(user_rmrrs[i].end_pfn + 1);
+        rmrr->scope.devices_cnt = user_rmrrs[i].dev_count;
+
+        if ( register_one_rmrr(rmrr) )
+        {
+            printk(XENLOG_ERR VTDPREFIX
+                   "Could not register RMMR range "ERMRRU_FMT"\n",
+                   ERMRRU_ARG(user_rmrrs[i]));
+            scope_devices_free(&rmrr->scope);
+            xfree(rmrr);
+        }
+    }
+    return 0;
+}
+
 #include <asm/tboot.h>
 /* ACPI tables may not be DMA protected by tboot, so use DMAR copy */
 /* SINIT saved in SinitMleData in TXT heap (which is DMA protected) */
@@ -868,6 +994,7 @@ int __init acpi_dmar_init(void)
 {
     acpi_physical_address dmar_addr;
     acpi_native_uint dmar_len;
+    int ret;
 
     if ( ACPI_SUCCESS(acpi_get_table_phys(ACPI_SIG_DMAR, 0,
                                           &dmar_addr, &dmar_len)) )
@@ -878,7 +1005,12 @@ int __init acpi_dmar_init(void)
         dmar_table = __va(dmar_addr);
     }
 
-    return parse_dmar_table(acpi_parse_dmar);
+    ret = parse_dmar_table(acpi_parse_dmar);
+
+    if ( !ret )
+        return add_user_rmrr();
+
+    return ret;
 }
 
 void acpi_dmar_reinstate(void)
@@ -937,3 +1069,70 @@ int intel_iommu_get_reserved_device_memory(iommu_grdm_t *func, void *ctxt)
 
     return 0;
 }
+
+/*
+ * Parse rmrr Xen command line options and add parsed devices and regions into
+ * acpi_rmrr_unit list to mapped as RMRRs parsed from ACPI.
+ * Format:
+ * rmrr=start<-end>=[s1]bdf1[,[s1]bdf2[,...]];start<-end>=[s2]bdf1[,[s2]bdf2[,...]]
+ * If the segment of the first device is not specified,
+ * segment zero will be used.
+ * If other segments are not specified, first device segment will be used.
+ * If a segment is specified for other than the first device, and it does not
+ * match the one specified for the first one, an error will be reported.
+ */
+static void __init parse_rmrr_param(const char *str)
+{
+    const char *s = str, *cur, *stmp;
+    unsigned int seg, bus, dev, func, dev_count;
+    unsigned long start, end;
+
+    do {
+        start = simple_strtoul(cur = s, &s, 0);
+        if ( cur == s )
+            break;
+
+        if ( *s == '-' )
+        {
+            end = simple_strtoul(cur = s + 1, &s, 0);
+            if ( cur == s )
+                break;
+        }
+        else
+            end = start;
+
+        user_rmrrs[nr_rmrr].base_pfn = start;
+        user_rmrrs[nr_rmrr].end_pfn = end;
+
+        if ( *s != '=' )
+            continue;
+
+        do {
+            bool def_seg = false;
+
+            stmp = parse_pci_seg(s + 1, &seg, &bus, &dev, &func, &def_seg);
+            if ( !stmp )
+                break;
+
+            /*
+             * Not specified.
+             * Segment will be replaced with one from first device.
+             */
+            if ( user_rmrrs[nr_rmrr].dev_count && def_seg )
+                seg = PCI_SEG(user_rmrrs[nr_rmrr].sbdf[0]);
+
+            /* Keep sbdf's even if they differ and later report an error. */
+            dev_count = user_rmrrs[nr_rmrr].dev_count;
+            user_rmrrs[nr_rmrr].sbdf[dev_count] = PCI_SBDF(seg, bus, dev, func);
+
+            user_rmrrs[nr_rmrr].dev_count++;
+            s = stmp;
+        } while ( *s == ',' &&
+                  user_rmrrs[nr_rmrr].dev_count < MAX_EXTRA_RMRR_DEV );
+
+        if ( user_rmrrs[nr_rmrr].dev_count )
+            nr_rmrr++;
+
+    } while ( *s++ == ';' && nr_rmrr < MAX_EXTRA_RMRR );
+}
+custom_param("rmrr", parse_rmrr_param);
