return ret;
}
+#define MAX_USER_RMRR_PAGES 16
+#define MAX_USER_RMRR 10
+
+/* RMRR units derived from command line rmrr option. */
+#define MAX_USER_RMRR_DEV 20
+struct user_rmrr {
+ struct list_head list;
+ unsigned long base_pfn, end_pfn;
+ unsigned int dev_count;
+ u32 sbdf[MAX_USER_RMRR_DEV];
+};
+
+static __initdata unsigned int nr_rmrr;
+static struct __initdata user_rmrr user_rmrrs[MAX_USER_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_USER_RMRR_PAGES )
+ {
+ printk(XENLOG_ERR VTDPREFIX
+ "RMRR range "ERMRRU_FMT" exceeds "\
+ __stringify(MAX_USER_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) )
+ {
+ 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);
+ /* Align the end_address to the end of the page */
+ rmrr->end_address = pfn_to_paddr(user_rmrrs[i].end_pfn) | ~PAGE_MASK;
+ 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) */
{
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)) )
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)
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_USER_RMRR_DEV );
+
+ if ( user_rmrrs[nr_rmrr].dev_count )
+ nr_rmrr++;
+
+ } while ( *s++ == ';' && nr_rmrr < MAX_USER_RMRR );
+}
+custom_param("rmrr", parse_rmrr_param);